|
@@ -13,90 +13,68 @@
|
|
|
<el-form-item label="套餐名称" prop="name">
|
|
<el-form-item label="套餐名称" prop="name">
|
|
|
<el-input v-model="formData.name" placeholder="请输入套餐名称" />
|
|
<el-input v-model="formData.name" placeholder="请输入套餐名称" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
- <!-- 套餐规格配置 -->
|
|
|
|
|
- <el-form-item prop="specificationsList">
|
|
|
|
|
|
|
+ <!-- 套餐规格列表 -->
|
|
|
|
|
+ <el-form-item label="套餐规格" v-if="currentEditId">
|
|
|
<div class="spec-section">
|
|
<div class="spec-section">
|
|
|
<div class="spec-header">
|
|
<div class="spec-header">
|
|
|
- <span class="spec-title">套餐规格:</span>
|
|
|
|
|
|
|
+ <span class="spec-title">规格列表:</span>
|
|
|
<el-button
|
|
<el-button
|
|
|
type="primary"
|
|
type="primary"
|
|
|
size="small"
|
|
size="small"
|
|
|
plain
|
|
plain
|
|
|
class="spec-add-btn"
|
|
class="spec-add-btn"
|
|
|
- @click="addSpecification"
|
|
|
|
|
|
|
+ @click="openSpecDialog"
|
|
|
>
|
|
>
|
|
|
<span class="spec-add-icon">+</span>
|
|
<span class="spec-add-icon">+</span>
|
|
|
<span>新增规格</span>
|
|
<span>新增规格</span>
|
|
|
</el-button>
|
|
</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="spec-row" v-for="(item, index) in formData.specificationsList" :key="index">
|
|
|
|
|
- <!-- 第一行:规格名称 / 价格 / 库存 / 套餐类型 -->
|
|
|
|
|
- <div class="spec-row-line spec-row-line--top">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="item.specName"
|
|
|
|
|
- placeholder="规格名称"
|
|
|
|
|
- class="spec-input spec-name"
|
|
|
|
|
- />
|
|
|
|
|
- <div class="spec-group">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model.number="item.price"
|
|
|
|
|
- :min="0"
|
|
|
|
|
- placeholder="价格"
|
|
|
|
|
- class="spec-input spec-price"
|
|
|
|
|
- />
|
|
|
|
|
- <span class="spec-unit">元</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-select v-model="item.packageType" placeholder="套餐类型"
|
|
|
|
|
- class="spec-input spec-select">
|
|
|
|
|
- <el-option v-for="opt in packageTypeOptions" :key="opt.value" :label="opt.label"
|
|
|
|
|
- :value="opt.value" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- <div class="spec-group">
|
|
|
|
|
- <span class="spec-label">库存:</span>
|
|
|
|
|
- <el-input-number v-model.number="item.stock" :min="0" placeholder="库存数量"
|
|
|
|
|
- class="spec-input" />
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 第二行:计费方式 / 次数 / 说明 / 删除 -->
|
|
|
|
|
- <div class="spec-row-line spec-row-line--bottom">
|
|
|
|
|
- <el-select
|
|
|
|
|
- v-model="item.chargeType"
|
|
|
|
|
- placeholder="计费方式"
|
|
|
|
|
- class="spec-input spec-select"
|
|
|
|
|
- >
|
|
|
|
|
- <el-option
|
|
|
|
|
- v-for="opt in chargeTypeOptions"
|
|
|
|
|
- :key="opt.value"
|
|
|
|
|
- :label="opt.label"
|
|
|
|
|
- :value="opt.value"
|
|
|
|
|
- />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- <div class="spec-group" v-if="showQuantity(item.chargeType)">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model.number="item.quantity"
|
|
|
|
|
- :min="0"
|
|
|
|
|
- placeholder="数量"
|
|
|
|
|
- class="spec-input spec-quantity"
|
|
|
|
|
- />
|
|
|
|
|
- <span class="spec-unit">{{ getUnitLabel(item.chargeType) }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="item.remark"
|
|
|
|
|
- placeholder="说明"
|
|
|
|
|
- class="spec-input spec-remark"
|
|
|
|
|
- />
|
|
|
|
|
- <el-button
|
|
|
|
|
- type="danger"
|
|
|
|
|
- size="small"
|
|
|
|
|
- text
|
|
|
|
|
- class="spec-delete-btn"
|
|
|
|
|
- @click="removeSpecification(index)"
|
|
|
|
|
- >
|
|
|
|
|
- <span class="spec-delete-icon">×</span>
|
|
|
|
|
- <span>删除</span>
|
|
|
|
|
- </el-button>
|
|
|
|
|
|
|
+ <div v-loading="specLoading" class="spec-list">
|
|
|
|
|
+ <el-table :data="specListData" border stripe style="width: 100%">
|
|
|
|
|
+ <el-table-column prop="spec_name" label="规格名称" min-width="120" />
|
|
|
|
|
+ <el-table-column prop="price" label="价格" min-width="100">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <span>¥{{ scope.row.price }}</span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column prop="stock" label="库存" min-width="80" />
|
|
|
|
|
+ <el-table-column prop="type" label="套餐类型" min-width="100">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-tag :type="scope.row.type === '1' ? 'primary' : 'success'">
|
|
|
|
|
+ {{ scope.row.type === '1' ? '个人套餐' : '企业套餐' }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column prop="card_type" label="卡类型" min-width="100">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ {{ getCardTypeLabel(scope.row.card_type) }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="次数/积分" min-width="100">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <span v-if="scope.row.card_type === '1' && scope.row.unit_value">
|
|
|
|
|
+ {{ scope.row.unit_value }}次
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span v-else-if="scope.row.card_type === '2' && scope.row.points_value">
|
|
|
|
|
+ {{ scope.row.points_value }}分
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span v-else>-</span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column prop="description" label="说明" min-width="150" show-overflow-tooltip />
|
|
|
|
|
+ <el-table-column label="操作" width="150" fixed="right">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-button type="primary" size="small" @click="editSpecItem(scope.row)">
|
|
|
|
|
+ 编辑
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button type="danger" size="small" @click="deleteSpecItem(scope.row)">
|
|
|
|
|
+ 删除
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ <div v-if="!specLoading && specListData.length === 0" class="spec-empty">
|
|
|
|
|
+ <el-empty description="暂无规格,请添加规格" :image-size="80" />
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -112,12 +90,17 @@
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
</el-dialog>
|
|
</el-dialog>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 规格弹框 -->
|
|
|
|
|
+ <SpecDialog ref="specDialogRef" @success="loadSpecList" />
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
-import { ref, reactive, computed } from 'vue'
|
|
|
|
|
-import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
|
|
|
|
|
|
|
+import { ref, reactive, watch } from 'vue'
|
|
|
|
|
+import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
|
|
|
import { useI18n } from 'vue-i18n'
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
|
+import { specList, deleteSpec } from '/@/api/backend/package/package'
|
|
|
|
|
+import SpecDialog from './specDialog.vue'
|
|
|
|
|
|
|
|
const { t } = useI18n()
|
|
const { t } = useI18n()
|
|
|
|
|
|
|
@@ -129,49 +112,25 @@ const dialogType = ref<'add' | 'edit'>('add')
|
|
|
const currentEditId = ref<number | null>(null)
|
|
const currentEditId = ref<number | null>(null)
|
|
|
|
|
|
|
|
const loading = ref(false)
|
|
const loading = ref(false)
|
|
|
|
|
+const specLoading = ref(false)
|
|
|
|
|
|
|
|
const formRef = ref<FormInstance>()
|
|
const formRef = ref<FormInstance>()
|
|
|
|
|
+const specDialogRef = ref<InstanceType<typeof SpecDialog>>()
|
|
|
|
|
|
|
|
const emits = defineEmits(['success'])
|
|
const emits = defineEmits(['success'])
|
|
|
|
|
|
|
|
-
|
|
|
|
|
const formData = reactive({
|
|
const formData = reactive({
|
|
|
name: '',
|
|
name: '',
|
|
|
- specificationsList: [] as SpecificationItem[],
|
|
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-
|
|
|
|
|
|
|
+// 规格列表
|
|
|
|
|
+const specListData = ref<any[]>([])
|
|
|
|
|
|
|
|
// 表单验证规则
|
|
// 表单验证规则
|
|
|
const rules: FormRules = {
|
|
const rules: FormRules = {
|
|
|
name: [
|
|
name: [
|
|
|
{ required: true, message: '请输入套餐名称', trigger: 'change' }
|
|
{ required: true, message: '请输入套餐名称', trigger: 'change' }
|
|
|
],
|
|
],
|
|
|
- specificationsList: [
|
|
|
|
|
- {
|
|
|
|
|
- validator: (_rule, value, callback) => {
|
|
|
|
|
- if (!value || !value.length) {
|
|
|
|
|
- return callback(new Error('请至少添加一个套餐规格'))
|
|
|
|
|
- }
|
|
|
|
|
- const invalid = value.some((item: SpecificationItem) => {
|
|
|
|
|
- const needQuantity = showQuantity(item.chargeType)
|
|
|
|
|
- return (
|
|
|
|
|
- !item.specName ||
|
|
|
|
|
- item.price == null ||
|
|
|
|
|
- item.stock == null ||
|
|
|
|
|
- !item.packageType ||
|
|
|
|
|
- !item.chargeType ||
|
|
|
|
|
- (needQuantity && item.quantity == null)
|
|
|
|
|
- )
|
|
|
|
|
- })
|
|
|
|
|
- if (invalid) {
|
|
|
|
|
- return callback(new Error('请完整填写每一行套餐规格'))
|
|
|
|
|
- }
|
|
|
|
|
- return callback()
|
|
|
|
|
- },
|
|
|
|
|
- trigger: 'change',
|
|
|
|
|
- },
|
|
|
|
|
- ]
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 打开对话框(新增)
|
|
// 打开对话框(新增)
|
|
@@ -180,11 +139,10 @@ const openDialog = () => {
|
|
|
dialogType.value = 'add'
|
|
dialogType.value = 'add'
|
|
|
currentEditId.value = null
|
|
currentEditId.value = null
|
|
|
resetForm()
|
|
resetForm()
|
|
|
-
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 编辑:接收列表行数据,回填到表单
|
|
// 编辑:接收列表行数据,回填到表单
|
|
|
-const editDialog = (row: TableRow & { id?: number; name?: string; specificationsList?: SpecificationItem[] }) => {
|
|
|
|
|
|
|
+const editDialog = (row: TableRow & { id?: number; name?: string }) => {
|
|
|
dialogVisible.value = true
|
|
dialogVisible.value = true
|
|
|
dialogType.value = 'edit'
|
|
dialogType.value = 'edit'
|
|
|
currentEditId.value = row.id ?? null
|
|
currentEditId.value = row.id ?? null
|
|
@@ -195,12 +153,9 @@ const editDialog = (row: TableRow & { id?: number; name?: string; specifications
|
|
|
// 回填基础信息
|
|
// 回填基础信息
|
|
|
formData.name = row.name ?? ''
|
|
formData.name = row.name ?? ''
|
|
|
|
|
|
|
|
- // 回填规格列表
|
|
|
|
|
- if (row.specificationsList && row.specificationsList.length) {
|
|
|
|
|
- formData.specificationsList = row.specificationsList.map((item) => ({ ...item }))
|
|
|
|
|
- } else {
|
|
|
|
|
- formData.specificationsList = []
|
|
|
|
|
- addSpecification()
|
|
|
|
|
|
|
+ // 加载规格列表
|
|
|
|
|
+ if (currentEditId.value) {
|
|
|
|
|
+ loadSpecList()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -208,8 +163,79 @@ const editDialog = (row: TableRow & { id?: number; name?: string; specifications
|
|
|
const resetForm = () => {
|
|
const resetForm = () => {
|
|
|
formRef.value?.clearValidate()
|
|
formRef.value?.clearValidate()
|
|
|
formData.name = ''
|
|
formData.name = ''
|
|
|
- formData.specificationsList = []
|
|
|
|
|
- addSpecification()
|
|
|
|
|
|
|
+ specListData.value = []
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 加载规格列表
|
|
|
|
|
+const loadSpecList = async () => {
|
|
|
|
|
+ if (!currentEditId.value) return
|
|
|
|
|
+
|
|
|
|
|
+ specLoading.value = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await specList({ package_id: currentEditId.value })
|
|
|
|
|
+ if (res.data && Array.isArray(res.data)) {
|
|
|
|
|
+ specListData.value = res.data
|
|
|
|
|
+ } else {
|
|
|
|
|
+ specListData.value = []
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ ElMessage({
|
|
|
|
|
+ type: 'error',
|
|
|
|
|
+ message: error?.msg || '加载规格列表失败',
|
|
|
|
|
+ })
|
|
|
|
|
+ specListData.value = []
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ specLoading.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 打开规格弹框(新增)
|
|
|
|
|
+const openSpecDialog = () => {
|
|
|
|
|
+ if (!currentEditId.value) {
|
|
|
|
|
+ ElMessage.warning('请先保存套餐')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ specDialogRef.value?.openDialog(currentEditId.value)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 编辑规格
|
|
|
|
|
+const editSpecItem = (row: any) => {
|
|
|
|
|
+ if (!currentEditId.value) return
|
|
|
|
|
+ specDialogRef.value?.editDialog(currentEditId.value, row)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 删除规格
|
|
|
|
|
+const deleteSpecItem = (row: any) => {
|
|
|
|
|
+ ElMessageBox.confirm(`确定删除规格"${row.spec_name}"吗?`, '提示', {
|
|
|
|
|
+ confirmButtonText: '确定',
|
|
|
|
|
+ cancelButtonText: '取消',
|
|
|
|
|
+ type: 'warning',
|
|
|
|
|
+ }).then(async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await deleteSpec({ id: row.id })
|
|
|
|
|
+ ElMessage.success('删除成功')
|
|
|
|
|
+ loadSpecList()
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ ElMessage({
|
|
|
|
|
+ type: 'error',
|
|
|
|
|
+ message: error?.msg || '删除失败,请重试',
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 获取卡类型标签
|
|
|
|
|
+const getCardTypeLabel = (cardType: string) => {
|
|
|
|
|
+ const map: Record<string, string> = {
|
|
|
|
|
+ '1': '次卡',
|
|
|
|
|
+ '2': '按积分',
|
|
|
|
|
+ '3': '日卡',
|
|
|
|
|
+ '4': '周卡',
|
|
|
|
|
+ '5': '季卡',
|
|
|
|
|
+ '6': '月卡',
|
|
|
|
|
+ '7': '年卡',
|
|
|
|
|
+ }
|
|
|
|
|
+ return map[cardType] || '-'
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 提交表单
|
|
// 提交表单
|
|
@@ -225,88 +251,45 @@ const onSubmit = async () => {
|
|
|
id: currentEditId.value,
|
|
id: currentEditId.value,
|
|
|
type: dialogType.value,
|
|
type: dialogType.value,
|
|
|
name: formData.name,
|
|
name: formData.name,
|
|
|
- specificationsList: formData.specificationsList
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
emits('success', submitData)
|
|
emits('success', submitData)
|
|
|
- handleClose()
|
|
|
|
|
|
|
+ // 注意:不在这里关闭弹框,由父组件处理成功后回调
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
ElMessage({
|
|
ElMessage({
|
|
|
type: 'error',
|
|
type: 'error',
|
|
|
- message: '账户添加失败,请重试!',
|
|
|
|
|
|
|
+ message: '保存失败,请重试!',
|
|
|
})
|
|
})
|
|
|
} finally {
|
|
} finally {
|
|
|
loading.value = false
|
|
loading.value = false
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 处理保存成功后的回调(由父组件调用)
|
|
|
|
|
+const handleSaveSuccess = (packageId: number) => {
|
|
|
|
|
+ // 切换到编辑模式
|
|
|
|
|
+ dialogType.value = 'edit'
|
|
|
|
|
+ currentEditId.value = packageId
|
|
|
|
|
+ // 加载规格列表
|
|
|
|
|
+ loadSpecList()
|
|
|
|
|
+ ElMessage.success('套餐保存成功,现在可以添加规格了')
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 关闭弹框
|
|
// 关闭弹框
|
|
|
const handleClose = () => {
|
|
const handleClose = () => {
|
|
|
dialogVisible.value = false
|
|
dialogVisible.value = false
|
|
|
resetForm()
|
|
resetForm()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 规格项类型
|
|
|
|
|
-interface SpecificationItem {
|
|
|
|
|
- specName: string
|
|
|
|
|
- price: number | undefined
|
|
|
|
|
- stock: number | undefined
|
|
|
|
|
- packageType: string
|
|
|
|
|
- chargeType: string
|
|
|
|
|
- quantity: number | undefined
|
|
|
|
|
- remark: string
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 套餐类型选项(值改为数字编码,方便与后端枚举对应)
|
|
|
|
|
-const packageTypeOptions = [
|
|
|
|
|
- { label: '个人套餐', value: '1' },
|
|
|
|
|
- { label: '团队套餐', value: '2' },
|
|
|
|
|
-]
|
|
|
|
|
-
|
|
|
|
|
-// 计费方式选项(值使用数字编码,便于与后端对接)
|
|
|
|
|
-const chargeTypeOptions = [
|
|
|
|
|
- { label: '按次数', value: '1' },
|
|
|
|
|
- { label: '按积分', value: '2' },
|
|
|
|
|
- { label: '日卡', value: '3' },
|
|
|
|
|
- { label: '周卡', value: '4' },
|
|
|
|
|
- { label: '月卡', value: '5' },
|
|
|
|
|
- { label: '季卡', value: '6' },
|
|
|
|
|
- { label: '年卡', value: '7' },
|
|
|
|
|
-]
|
|
|
|
|
-
|
|
|
|
|
-// 新增一行规格
|
|
|
|
|
-const addSpecification = () => {
|
|
|
|
|
- formData.specificationsList.push({
|
|
|
|
|
- specName: '',
|
|
|
|
|
- price: undefined,
|
|
|
|
|
- stock: undefined,
|
|
|
|
|
- packageType: packageTypeOptions[0].value,
|
|
|
|
|
- chargeType: chargeTypeOptions[0].value,
|
|
|
|
|
- quantity: undefined,
|
|
|
|
|
- remark: '',
|
|
|
|
|
- })
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 删除一行规格
|
|
|
|
|
-const removeSpecification = (index: number) => {
|
|
|
|
|
- formData.specificationsList.splice(index, 1)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 是否需要展示“次数 / 积分”等数量输入框
|
|
|
|
|
-const showQuantity = (chargeType: string) => {
|
|
|
|
|
- // 1: 按次数,2: 按积分
|
|
|
|
|
- return chargeType === '1' || chargeType === '2'
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 根据计费方式返回数量单位
|
|
|
|
|
-const getUnitLabel = (chargeType: string) => {
|
|
|
|
|
- if (chargeType === '1') return '次'
|
|
|
|
|
- if (chargeType === '2') return '分'
|
|
|
|
|
- return ''
|
|
|
|
|
-}
|
|
|
|
|
|
|
+// 监听对话框打开,如果是编辑模式且有ID,加载规格列表
|
|
|
|
|
+watch([dialogVisible, currentEditId], ([visible, id]) => {
|
|
|
|
|
+ if (visible && dialogType.value === 'edit' && id) {
|
|
|
|
|
+ loadSpecList()
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
|
|
|
// 导出方法
|
|
// 导出方法
|
|
|
-defineExpose({ openDialog, editDialog });
|
|
|
|
|
|
|
+defineExpose({ openDialog, editDialog, handleSaveSuccess })
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
<style scoped lang="scss">
|
|
@@ -322,7 +305,7 @@ defineExpose({ openDialog, editDialog });
|
|
|
display: flex;
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- margin-bottom: 8px;
|
|
|
|
|
|
|
+ margin-bottom: 12px;
|
|
|
|
|
|
|
|
.spec-title {
|
|
.spec-title {
|
|
|
font-weight: 600;
|
|
font-weight: 600;
|
|
@@ -343,94 +326,17 @@ defineExpose({ openDialog, editDialog });
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .spec-row {
|
|
|
|
|
- position: relative;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- row-gap: 6px;
|
|
|
|
|
- margin-bottom: 12px;
|
|
|
|
|
- padding: 10px 12px 10px 36px;
|
|
|
|
|
- border-radius: 6px;
|
|
|
|
|
- background-color: #fff;
|
|
|
|
|
- box-shadow: 0 0 0 1px #f0f0f0;
|
|
|
|
|
-
|
|
|
|
|
- .spec-row-line {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- column-gap: 10px;
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .spec-input {
|
|
|
|
|
- margin-right: 0;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .spec-name {
|
|
|
|
|
- width: 140px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .spec-price {
|
|
|
|
|
- width: 120px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .spec-stock {
|
|
|
|
|
- width: 100px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .spec-select {
|
|
|
|
|
- width: 140px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .spec-quantity {
|
|
|
|
|
- width: 120px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .spec-remark {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- min-width: 260px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .spec-group {
|
|
|
|
|
- display: inline-flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- column-gap: 4px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .spec-delete-btn {
|
|
|
|
|
- display: inline-flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 2px;
|
|
|
|
|
- padding: 0 6px;
|
|
|
|
|
-
|
|
|
|
|
- .spec-delete-icon {
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- transform: translateY(-1px);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .spec-unit,
|
|
|
|
|
- .spec-label {
|
|
|
|
|
- color: #666;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ .spec-list {
|
|
|
|
|
+ min-height: 200px;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- .spec-badge {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- left: 10px;
|
|
|
|
|
- top: 50%;
|
|
|
|
|
- transform: translateY(-50%);
|
|
|
|
|
- width: 20px;
|
|
|
|
|
- height: 20px;
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
- background: linear-gradient(135deg, #409eff, #66b1ff);
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.8);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ .spec-empty {
|
|
|
|
|
+ padding: 40px 0;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ .spec-tip {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|