|
@@ -7,12 +7,18 @@
|
|
|
<span class="title">服务管理 (列表)</span>
|
|
<span class="title">服务管理 (列表)</span>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="header-right">
|
|
<div class="header-right">
|
|
|
|
|
+ <el-select v-model="queryParams.classificationId" placeholder="全部分类" clearable @change="handleQuery"
|
|
|
|
|
+ style="width: 180px; margin-right: 12px;">
|
|
|
|
|
+ <el-option label="全部" :value="undefined" />
|
|
|
|
|
+ <el-option v-for="item in classificationList" :key="item.id" :label="item.name" :value="item.id" />
|
|
|
|
|
+ </el-select>
|
|
|
<el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['service:list:add']">新增服务</el-button>
|
|
<el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['service:list:add']">新增服务</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
- <el-table v-loading="loading" :data="serviceList" style="width: 100%" :header-cell-style="{ background: '#f8f9fb', color: '#606266' }">
|
|
|
|
|
|
|
+ <el-table v-loading="loading" :data="serviceList" style="width: 100%"
|
|
|
|
|
+ :header-cell-style="{ background: '#f8f9fb', color: '#606266' }">
|
|
|
<el-table-column label="排序权重" align="center" prop="sort" width="100" />
|
|
<el-table-column label="排序权重" align="center" prop="sort" width="100" />
|
|
|
<el-table-column label="服务名称" prop="name" min-width="150">
|
|
<el-table-column label="服务名称" prop="name" min-width="150">
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
@@ -26,6 +32,11 @@
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
|
|
+ <el-table-column label="所属分类" align="center" width="120">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <span>{{ getClassificationName(scope.row.classificationId) }}</span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
<el-table-column label="服务模式" align="center" width="150">
|
|
<el-table-column label="服务模式" align="center" width="150">
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
<el-tag :type="getModeTagType(scope.row.mode)" size="small" effect="light" class="mode-tag">
|
|
<el-tag :type="getModeTagType(scope.row.mode)" size="small" effect="light" class="mode-tag">
|
|
@@ -42,21 +53,18 @@
|
|
|
<el-table-column label="操作" align="right" width="140" fixed="right">
|
|
<el-table-column label="操作" align="right" width="140" fixed="right">
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
<div class="op-btns">
|
|
<div class="op-btns">
|
|
|
- <el-button link type="primary" @click="handleUpdate(scope.row)" v-hasPermi="['service:list:edit']">编辑</el-button>
|
|
|
|
|
- <el-button link type="danger" @click="handleDelete(scope.row)" v-hasPermi="['service:list:remove']">删除</el-button>
|
|
|
|
|
|
|
+ <el-button link type="primary" @click="handleUpdate(scope.row)"
|
|
|
|
|
+ v-hasPermi="['service:list:edit']">编辑</el-button>
|
|
|
|
|
+ <el-button link type="danger" @click="handleDelete(scope.row)"
|
|
|
|
|
+ v-hasPermi="['service:list:remove']">删除</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
</el-table>
|
|
</el-table>
|
|
|
|
|
|
|
|
<div class="pagination-container">
|
|
<div class="pagination-container">
|
|
|
- <pagination
|
|
|
|
|
- v-show="total > 0"
|
|
|
|
|
- v-model:total="total"
|
|
|
|
|
- v-model:page="queryParams.pageNum"
|
|
|
|
|
- v-model:limit="queryParams.pageSize"
|
|
|
|
|
- @pagination="getList"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <pagination v-show="total > 0" v-model:total="total" v-model:page="queryParams.pageNum"
|
|
|
|
|
+ v-model:limit="queryParams.pageSize" @pagination="getList" />
|
|
|
</div>
|
|
</div>
|
|
|
</el-card>
|
|
</el-card>
|
|
|
|
|
|
|
@@ -76,9 +84,20 @@
|
|
|
</el-radio>
|
|
</el-radio>
|
|
|
</el-radio-group>
|
|
</el-radio-group>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
+ <el-form-item label="所属分类" prop="classificationId">
|
|
|
|
|
+ <el-select v-model="form.classificationId" placeholder="请选择所属分类" clearable style="width: 100%">
|
|
|
|
|
+ <el-option v-for="item in classificationList" :key="item.id" :label="item.name" :value="item.id" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
<el-form-item label="排序权重" prop="sort">
|
|
<el-form-item label="排序权重" prop="sort">
|
|
|
<el-input-number v-model="form.sort" :min="0" controls-position="right" style="width: 100%" />
|
|
<el-input-number v-model="form.sort" :min="0" controls-position="right" style="width: 100%" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
+ <el-form-item label="服务介绍" prop="introduction">
|
|
|
|
|
+ <editor v-model="form.introduction" :min-height="300" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="下单须知" prop="orderInstruction">
|
|
|
|
|
+ <editor v-model="form.orderInstruction" :min-height="300" />
|
|
|
|
|
+ </el-form-item>
|
|
|
<el-form-item label="备注说明" prop="remark">
|
|
<el-form-item label="备注说明" prop="remark">
|
|
|
<el-input v-model="form.remark" placeholder="请输入备注说明" type="textarea" :rows="2" />
|
|
<el-input v-model="form.remark" placeholder="请输入备注说明" type="textarea" :rows="2" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
@@ -115,6 +134,8 @@ import { listService, getService, delService, addService, updateService } from '
|
|
|
import { ServiceVO, ServiceQuery, ServiceForm } from '@/api/service/list/types';
|
|
import { ServiceVO, ServiceQuery, ServiceForm } from '@/api/service/list/types';
|
|
|
import { list as listMode } from '@/api/service/mode';
|
|
import { list as listMode } from '@/api/service/mode';
|
|
|
import { SysServiceModeVo } from '@/api/service/mode/types';
|
|
import { SysServiceModeVo } from '@/api/service/mode/types';
|
|
|
|
|
+import { listAllClassification } from '@/api/service/classification';
|
|
|
|
|
+import { ClassificationVO } from '@/api/service/classification/types';
|
|
|
|
|
|
|
|
interface ClockInRemarkItem {
|
|
interface ClockInRemarkItem {
|
|
|
step: number;
|
|
step: number;
|
|
@@ -130,6 +151,7 @@ const buttonLoading = ref(false);
|
|
|
const loading = ref(true);
|
|
const loading = ref(true);
|
|
|
const total = ref(0);
|
|
const total = ref(0);
|
|
|
const modeList = ref<SysServiceModeVo[]>([]);
|
|
const modeList = ref<SysServiceModeVo[]>([]);
|
|
|
|
|
+const classificationList = ref<ClassificationVO[]>([]);
|
|
|
|
|
|
|
|
const serviceFormRef = ref<ElFormInstance>();
|
|
const serviceFormRef = ref<ElFormInstance>();
|
|
|
|
|
|
|
@@ -146,6 +168,9 @@ const initFormData: ServiceForm = {
|
|
|
sort: undefined,
|
|
sort: undefined,
|
|
|
remark: undefined,
|
|
remark: undefined,
|
|
|
clockInRemark: undefined,
|
|
clockInRemark: undefined,
|
|
|
|
|
+ introduction: undefined,
|
|
|
|
|
+ orderInstruction: undefined,
|
|
|
|
|
+ classificationId: undefined,
|
|
|
tenantId: undefined
|
|
tenantId: undefined
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -154,6 +179,7 @@ const data = reactive<PageData<ServiceForm, ServiceQuery>>({
|
|
|
queryParams: {
|
|
queryParams: {
|
|
|
pageNum: 1,
|
|
pageNum: 1,
|
|
|
pageSize: 10,
|
|
pageSize: 10,
|
|
|
|
|
+ classificationId: undefined,
|
|
|
params: {}
|
|
params: {}
|
|
|
},
|
|
},
|
|
|
rules: {
|
|
rules: {
|
|
@@ -174,6 +200,12 @@ const getList = async () => {
|
|
|
loading.value = false;
|
|
loading.value = false;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+/** 搜索按钮操作 */
|
|
|
|
|
+const handleQuery = () => {
|
|
|
|
|
+ queryParams.value.pageNum = 1;
|
|
|
|
|
+ getList();
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
/** 获取服务模式列表 */
|
|
/** 获取服务模式列表 */
|
|
|
const getModeList = async () => {
|
|
const getModeList = async () => {
|
|
|
try {
|
|
try {
|
|
@@ -184,6 +216,16 @@ const getModeList = async () => {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+/** 获取分类列表 */
|
|
|
|
|
+const getClassificationList = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await listAllClassification();
|
|
|
|
|
+ classificationList.value = res.data || [];
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取分类列表失败:', error);
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
/** 根据服务模式获取 Tag 类型 */
|
|
/** 根据服务模式获取 Tag 类型 */
|
|
|
const getModeTagType = (value: any) => {
|
|
const getModeTagType = (value: any) => {
|
|
|
const map: any = { 'round': 'primary', 'one-way': 'info' };
|
|
const map: any = { 'round': 'primary', 'one-way': 'info' };
|
|
@@ -196,6 +238,13 @@ const getModeLabel = (value: any): string => {
|
|
|
return mode ? mode.label : value;
|
|
return mode ? mode.label : value;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+/** 根据分类ID获取分类名称 */
|
|
|
|
|
+const getClassificationName = (classificationId: number): string => {
|
|
|
|
|
+ if (!classificationId) return '-';
|
|
|
|
|
+ const classification = classificationList.value.find((item) => item.id === classificationId);
|
|
|
|
|
+ return classification ? classification.name : '-';
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
/** 取消按钮 */
|
|
/** 取消按钮 */
|
|
|
const cancel = () => {
|
|
const cancel = () => {
|
|
|
reset();
|
|
reset();
|
|
@@ -230,13 +279,30 @@ const handleUpdate = async (row: ServiceVO) => {
|
|
|
reset();
|
|
reset();
|
|
|
const res = await getService(row.id);
|
|
const res = await getService(row.id);
|
|
|
Object.assign(form.value, res.data);
|
|
Object.assign(form.value, res.data);
|
|
|
|
|
+
|
|
|
|
|
+ // 解码富文本字段
|
|
|
|
|
+ if (form.value.introduction) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ form.value.introduction = decodeURIComponent(atob(form.value.introduction));
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('服务介绍解码失败:', e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (form.value.orderInstruction) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ form.value.orderInstruction = decodeURIComponent(atob(form.value.orderInstruction));
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('下单须知解码失败:', e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (form.value.clockInRemark) {
|
|
if (form.value.clockInRemark) {
|
|
|
try {
|
|
try {
|
|
|
const parsed = JSON.parse(form.value.clockInRemark);
|
|
const parsed = JSON.parse(form.value.clockInRemark);
|
|
|
if (Array.isArray(parsed)) {
|
|
if (Array.isArray(parsed)) {
|
|
|
clockInRemarkList.value = getInitialClockInRemarks().map((item, index) => {
|
|
clockInRemarkList.value = getInitialClockInRemarks().map((item, index) => {
|
|
|
return parsed[index] ? { ...item, ...parsed[index] } : item;
|
|
return parsed[index] ? { ...item, ...parsed[index] } : item;
|
|
|
- });
|
|
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
clockInRemarkList.value = getInitialClockInRemarks();
|
|
clockInRemarkList.value = getInitialClockInRemarks();
|
|
@@ -251,11 +317,21 @@ const submitForm = () => {
|
|
|
serviceFormRef.value?.validate(async (valid: boolean) => {
|
|
serviceFormRef.value?.validate(async (valid: boolean) => {
|
|
|
if (valid) {
|
|
if (valid) {
|
|
|
form.value.clockInRemark = clockInRemarkList.value.length > 0 ? JSON.stringify(clockInRemarkList.value) : undefined;
|
|
form.value.clockInRemark = clockInRemarkList.value.length > 0 ? JSON.stringify(clockInRemarkList.value) : undefined;
|
|
|
|
|
+
|
|
|
|
|
+ // 编码富文本字段
|
|
|
|
|
+ const submitData = { ...form.value };
|
|
|
|
|
+ if (submitData.introduction) {
|
|
|
|
|
+ submitData.introduction = btoa(encodeURIComponent(submitData.introduction));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (submitData.orderInstruction) {
|
|
|
|
|
+ submitData.orderInstruction = btoa(encodeURIComponent(submitData.orderInstruction));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
buttonLoading.value = true;
|
|
buttonLoading.value = true;
|
|
|
- if (form.value.id) {
|
|
|
|
|
- await updateService(form.value).finally(() => (buttonLoading.value = false));
|
|
|
|
|
|
|
+ if (submitData.id) {
|
|
|
|
|
+ await updateService(submitData).finally(() => (buttonLoading.value = false));
|
|
|
} else {
|
|
} else {
|
|
|
- await addService(form.value).finally(() => (buttonLoading.value = false));
|
|
|
|
|
|
|
+ await addService(submitData).finally(() => (buttonLoading.value = false));
|
|
|
}
|
|
}
|
|
|
proxy?.$modal.msgSuccess('操作成功');
|
|
proxy?.$modal.msgSuccess('操作成功');
|
|
|
dialog.visible = false;
|
|
dialog.visible = false;
|
|
@@ -276,6 +352,7 @@ const handleDelete = async (row: ServiceVO) => {
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
getList();
|
|
getList();
|
|
|
getModeList();
|
|
getModeList();
|
|
|
|
|
+ getClassificationList();
|
|
|
});
|
|
});
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
@@ -289,7 +366,7 @@ onMounted(() => {
|
|
|
.table-card {
|
|
.table-card {
|
|
|
border: none;
|
|
border: none;
|
|
|
border-radius: 8px;
|
|
border-radius: 8px;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
:deep(.el-card__header) {
|
|
:deep(.el-card__header) {
|
|
|
padding: 20px 24px;
|
|
padding: 20px 24px;
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
@@ -347,18 +424,21 @@ onMounted(() => {
|
|
|
margin-bottom: 12px;
|
|
margin-bottom: 12px;
|
|
|
background-color: #f8f9fb;
|
|
background-color: #f8f9fb;
|
|
|
border: 1px solid #ebeef5;
|
|
border: 1px solid #ebeef5;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
.step-header {
|
|
.step-header {
|
|
|
font-size: 13px;
|
|
font-size: 13px;
|
|
|
font-weight: 600;
|
|
font-weight: 600;
|
|
|
color: #606266;
|
|
color: #606266;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
.inner-form-item {
|
|
.inner-form-item {
|
|
|
margin-bottom: 12px;
|
|
margin-bottom: 12px;
|
|
|
- &.last { margin-bottom: 0; }
|
|
|
|
|
|
|
+
|
|
|
|
|
+ &.last {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
:deep(.el-card__header) {
|
|
:deep(.el-card__header) {
|
|
|
padding: 8px 16px;
|
|
padding: 8px 16px;
|
|
|
background-color: #f0f2f5;
|
|
background-color: #f0f2f5;
|
|
@@ -367,11 +447,11 @@ onMounted(() => {
|
|
|
|
|
|
|
|
:deep(.el-table) {
|
|
:deep(.el-table) {
|
|
|
--el-table-border-color: #f0f0f0;
|
|
--el-table-border-color: #f0f0f0;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
th.el-table__cell {
|
|
th.el-table__cell {
|
|
|
font-weight: 600;
|
|
font-weight: 600;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
td.el-table__cell {
|
|
td.el-table__cell {
|
|
|
padding: 12px 0;
|
|
padding: 12px 0;
|
|
|
}
|
|
}
|