|
@@ -27,7 +27,7 @@
|
|
|
<span v-else>-</span>
|
|
<span v-else>-</span>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
- <el-table-column prop="service_id" label="客服ID" width="100" />
|
|
|
|
|
|
|
+ <el-table-column prop="kefu_id" label="客服ID" width="100" />
|
|
|
<el-table-column prop="price" label="价格" width="110" />
|
|
<el-table-column prop="price" label="价格" width="110" />
|
|
|
<el-table-column label="状态" width="120">
|
|
<el-table-column label="状态" width="120">
|
|
|
<template #default="{ row }">
|
|
<template #default="{ row }">
|
|
@@ -79,14 +79,35 @@
|
|
|
<el-form-item label="说明" prop="explain">
|
|
<el-form-item label="说明" prop="explain">
|
|
|
<el-input v-model="form.explain" type="textarea" :rows="4" />
|
|
<el-input v-model="form.explain" type="textarea" :rows="4" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
- <el-form-item label="素材" prop="materialText">
|
|
|
|
|
- <el-input v-model="form.materialText" placeholder="多个素材ID用英文逗号分隔,如 11,12,13" />
|
|
|
|
|
|
|
+ <el-form-item label="素材" prop="materialFiles">
|
|
|
|
|
+ <scUploadFile
|
|
|
|
|
+ v-model="form.materialFiles"
|
|
|
|
|
+ :apiObj="$API.common.uploadFile"
|
|
|
|
|
+ :multiple="true"
|
|
|
|
|
+ :limit="20"
|
|
|
|
|
+ tip="请上传素材文件,支持多文件,上传后自动写入素材链接"
|
|
|
|
|
+ btnTxt="上传素材"
|
|
|
|
|
+ />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item label="价格" prop="price">
|
|
<el-form-item label="价格" prop="price">
|
|
|
<el-input v-model="form.price" />
|
|
<el-input v-model="form.price" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
- <el-form-item label="客服ID" prop="service_id">
|
|
|
|
|
- <el-input v-model="form.service_id" />
|
|
|
|
|
|
|
+ <el-form-item label="客服ID" prop="kefu_id">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="form.kefu_id"
|
|
|
|
|
+ placeholder="请选择客服"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ filterable
|
|
|
|
|
+ :loading="kefuLoading"
|
|
|
|
|
+ style="width: 100%;"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="item in kefuOptions"
|
|
|
|
|
+ :key="item.id"
|
|
|
|
|
+ :label="item.label"
|
|
|
|
|
+ :value="item.value"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item label="截止时间" prop="endDate">
|
|
<el-form-item label="截止时间" prop="endDate">
|
|
|
<el-date-picker
|
|
<el-date-picker
|
|
@@ -119,6 +140,36 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
|
|
+function formatMaterialLabels(material) {
|
|
|
|
|
+ if (!Array.isArray(material) || !material.length) return [];
|
|
|
|
|
+ return material
|
|
|
|
|
+ .map((m) => {
|
|
|
|
|
+ if (m == null) return "";
|
|
|
|
|
+ if (typeof m === "string") return m.trim();
|
|
|
|
|
+ return String(m.fileName || m.name || m.src || m.url || "").trim();
|
|
|
|
|
+ })
|
|
|
|
|
+ .filter(Boolean);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function normalizeMaterialFiles(material) {
|
|
|
|
|
+ if (!Array.isArray(material) || !material.length) return [];
|
|
|
|
|
+ return material
|
|
|
|
|
+ .map((m) => {
|
|
|
|
|
+ if (typeof m === "string") {
|
|
|
|
|
+ const url = m.trim();
|
|
|
|
|
+ if (!url) return null;
|
|
|
|
|
+ const parts = url.split(/[/\\]/);
|
|
|
|
|
+ return { name: parts[parts.length - 1] || url, url };
|
|
|
|
|
+ }
|
|
|
|
|
+ const src = String(m.src || m.url || "").trim();
|
|
|
|
|
+ if (!src) return null;
|
|
|
|
|
+ const fileName = String(m.fileName || m.name || "").trim();
|
|
|
|
|
+ const name = fileName || src.split(/[/\\]/).pop() || src;
|
|
|
|
|
+ return { name, url: src };
|
|
|
|
|
+ })
|
|
|
|
|
+ .filter(Boolean);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
export default {
|
|
export default {
|
|
|
name: "taskList",
|
|
name: "taskList",
|
|
|
data() {
|
|
data() {
|
|
@@ -127,6 +178,8 @@ export default {
|
|
|
saving: false,
|
|
saving: false,
|
|
|
list: [],
|
|
list: [],
|
|
|
total: 0,
|
|
total: 0,
|
|
|
|
|
+ kefuLoading: false,
|
|
|
|
|
+ kefuOptions: [],
|
|
|
query: {
|
|
query: {
|
|
|
page: 1,
|
|
page: 1,
|
|
|
limit: 15
|
|
limit: 15
|
|
@@ -137,14 +190,23 @@ export default {
|
|
|
rules: {
|
|
rules: {
|
|
|
title: [{ required: true, message: "请输入标题", trigger: "blur" }],
|
|
title: [{ required: true, message: "请输入标题", trigger: "blur" }],
|
|
|
explain: [{ required: true, message: "请输入说明", trigger: "blur" }],
|
|
explain: [{ required: true, message: "请输入说明", trigger: "blur" }],
|
|
|
- materialText: [{ required: true, message: "请输入素材ID", trigger: "blur" }],
|
|
|
|
|
|
|
+ materialFiles: [
|
|
|
|
|
+ {
|
|
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
|
|
+ if (Array.isArray(value) && value.length) callback();
|
|
|
|
|
+ else callback(new Error("请上传素材文件"));
|
|
|
|
|
+ },
|
|
|
|
|
+ trigger: "change"
|
|
|
|
|
+ }
|
|
|
|
|
+ ],
|
|
|
price: [{ required: true, message: "请输入价格", trigger: "blur" }],
|
|
price: [{ required: true, message: "请输入价格", trigger: "blur" }],
|
|
|
- service_id: [{ required: true, message: "请输入客服ID", trigger: "blur" }],
|
|
|
|
|
|
|
+ kefu_id: [{ required: true, message: "请选择客服", trigger: "change" }],
|
|
|
endDate: [{ required: true, message: "请选择截止时间", trigger: "change" }]
|
|
endDate: [{ required: true, message: "请选择截止时间", trigger: "change" }]
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
},
|
|
},
|
|
|
created() {
|
|
created() {
|
|
|
|
|
+ this.getKefuOptions();
|
|
|
this.getList();
|
|
this.getList();
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
@@ -153,10 +215,9 @@ export default {
|
|
|
id: "",
|
|
id: "",
|
|
|
title: "",
|
|
title: "",
|
|
|
explain: "",
|
|
explain: "",
|
|
|
- material: [],
|
|
|
|
|
- materialText: "",
|
|
|
|
|
|
|
+ materialFiles: [],
|
|
|
price: "",
|
|
price: "",
|
|
|
- service_id: "",
|
|
|
|
|
|
|
+ kefu_id: "",
|
|
|
endDate: "",
|
|
endDate: "",
|
|
|
end_time: "",
|
|
end_time: "",
|
|
|
status: 1
|
|
status: 1
|
|
@@ -180,7 +241,7 @@ export default {
|
|
|
const rawList = data.data || data.list || [];
|
|
const rawList = data.data || data.list || [];
|
|
|
this.list = rawList.map((item) => ({
|
|
this.list = rawList.map((item) => ({
|
|
|
...item,
|
|
...item,
|
|
|
- materialDisplay: Array.isArray(item.material) ? item.material : [],
|
|
|
|
|
|
|
+ materialDisplay: formatMaterialLabels(item.material),
|
|
|
// 后端字段为 pirce,这里统一映射给表格展示
|
|
// 后端字段为 pirce,这里统一映射给表格展示
|
|
|
price: item.price || item.pirce || ""
|
|
price: item.price || item.pirce || ""
|
|
|
}));
|
|
}));
|
|
@@ -189,6 +250,25 @@ export default {
|
|
|
this.loading = false;
|
|
this.loading = false;
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
+ async getKefuOptions() {
|
|
|
|
|
+ this.kefuLoading = true;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await this.$API.kefu.list.get({ page: 1, limit: 200 });
|
|
|
|
|
+ if (res.code !== 1) {
|
|
|
|
|
+ this.$message.error(res.msg || "获取客服列表失败");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const data = res.data || {};
|
|
|
|
|
+ const rawList = data.data || data.list || [];
|
|
|
|
|
+ this.kefuOptions = rawList.map((item) => ({
|
|
|
|
|
+ id: item.id,
|
|
|
|
|
+ value: String(item.id),
|
|
|
|
|
+ label: item.name ? `${item.name}(id:${item.id})` : String(item.id)
|
|
|
|
|
+ }));
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.kefuLoading = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
handlePageChange(page) {
|
|
handlePageChange(page) {
|
|
|
this.query.page = page;
|
|
this.query.page = page;
|
|
|
this.getList();
|
|
this.getList();
|
|
@@ -213,16 +293,17 @@ export default {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
const info = res.data || {};
|
|
const info = res.data || {};
|
|
|
- const material = Array.isArray(info.material) ? info.material : [];
|
|
|
|
|
|
|
+ const materialFiles = normalizeMaterialFiles(
|
|
|
|
|
+ Array.isArray(info.material) ? info.material : []
|
|
|
|
|
+ );
|
|
|
const endTime = info.end_time ? Number(info.end_time) * 1000 : "";
|
|
const endTime = info.end_time ? Number(info.end_time) * 1000 : "";
|
|
|
this.form = {
|
|
this.form = {
|
|
|
id: info.id || row.id,
|
|
id: info.id || row.id,
|
|
|
title: info.title || "",
|
|
title: info.title || "",
|
|
|
explain: info.explain || "",
|
|
explain: info.explain || "",
|
|
|
- material,
|
|
|
|
|
- materialText: material.join(","),
|
|
|
|
|
|
|
+ materialFiles,
|
|
|
price: info.price || info.pirce || "",
|
|
price: info.price || info.pirce || "",
|
|
|
- service_id: String(info.service_id || ""),
|
|
|
|
|
|
|
+ kefu_id: String(info.kefu_id || ""),
|
|
|
endDate: endTime ? String(endTime) : "",
|
|
endDate: endTime ? String(endTime) : "",
|
|
|
end_time: String(info.end_time || ""),
|
|
end_time: String(info.end_time || ""),
|
|
|
status: Number(info.status ?? 1)
|
|
status: Number(info.status ?? 1)
|
|
@@ -233,18 +314,18 @@ export default {
|
|
|
if (!valid) return false;
|
|
if (!valid) return false;
|
|
|
this.saving = true;
|
|
this.saving = true;
|
|
|
try {
|
|
try {
|
|
|
- const material = (this.form.materialText || "")
|
|
|
|
|
- .split(",")
|
|
|
|
|
- .map((item) => item.trim())
|
|
|
|
|
- .filter(Boolean);
|
|
|
|
|
|
|
+ const material = (this.form.materialFiles || []).map((f) => ({
|
|
|
|
|
+ fileName: f.name || "",
|
|
|
|
|
+ src: f.url || ""
|
|
|
|
|
+ })).filter((m) => m.src);
|
|
|
const endTimeSec = this.form.endDate ? Math.floor(Number(this.form.endDate) / 1000) : "";
|
|
const endTimeSec = this.form.endDate ? Math.floor(Number(this.form.endDate) / 1000) : "";
|
|
|
const payload = {
|
|
const payload = {
|
|
|
title: this.form.title,
|
|
title: this.form.title,
|
|
|
explain: this.form.explain,
|
|
explain: this.form.explain,
|
|
|
material,
|
|
material,
|
|
|
- pirce: this.form.price,
|
|
|
|
|
|
|
+ price: this.form.price,
|
|
|
end_time: String(endTimeSec),
|
|
end_time: String(endTimeSec),
|
|
|
- service_id: this.form.service_id,
|
|
|
|
|
|
|
+ kefu_id: this.form.kefu_id,
|
|
|
status: String(this.form.status)
|
|
status: String(this.form.status)
|
|
|
};
|
|
};
|
|
|
if (this.form.id) payload.id = this.form.id;
|
|
if (this.form.id) payload.id = this.form.id;
|