|
|
@@ -0,0 +1,311 @@
|
|
|
+<template>
|
|
|
+ <el-main>
|
|
|
+ <el-card shadow="never">
|
|
|
+ <template #header>
|
|
|
+ <div class="header-row">
|
|
|
+ <span>任务管理</span>
|
|
|
+ <el-button type="primary" @click="openCreate">新增任务</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <el-table v-loading="loading" :data="list" border>
|
|
|
+ <el-table-column prop="id" label="ID" width="80" />
|
|
|
+ <el-table-column prop="title" label="标题" min-width="180" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="explain" label="说明" min-width="220" show-overflow-tooltip />
|
|
|
+ <el-table-column label="素材" min-width="180">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <div v-if="row.materialDisplay.length">
|
|
|
+ <el-tag
|
|
|
+ v-for="(item, index) in row.materialDisplay"
|
|
|
+ :key="`${row.id}-${index}`"
|
|
|
+ size="small"
|
|
|
+ class="material-tag"
|
|
|
+ >
|
|
|
+ {{ item }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ <span v-else>-</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="service_id" label="客服ID" width="100" />
|
|
|
+ <el-table-column prop="price" label="价格" width="110" />
|
|
|
+ <el-table-column label="状态" width="120">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-switch
|
|
|
+ :model-value="Number(row.status)"
|
|
|
+ :active-value="1"
|
|
|
+ :inactive-value="0"
|
|
|
+ active-text="开启"
|
|
|
+ inactive-text="关闭"
|
|
|
+ inline-prompt
|
|
|
+ active-color="#13ce66"
|
|
|
+ inactive-color="#ff4949"
|
|
|
+ @change="(val) => changeStatus(row, val)"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="end_time" label="截稿日期" min-width="160">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ formatTime(row.end_time) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="140" fixed="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-button size="small" @click="openEdit(row)">编辑</el-button>
|
|
|
+ <el-button size="small" type="danger" @click="removeTask(row)">删除</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <div class="pager">
|
|
|
+ <el-pagination
|
|
|
+ background
|
|
|
+ layout="total, prev, pager, next, sizes"
|
|
|
+ :total="total"
|
|
|
+ :current-page="query.page"
|
|
|
+ :page-size="query.limit"
|
|
|
+ :page-sizes="[5, 10, 20, 50]"
|
|
|
+ @current-change="handlePageChange"
|
|
|
+ @size-change="handleSizeChange"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-dialog v-model="dialogVisible" :title="dialogTitle" width="640px" destroy-on-close>
|
|
|
+ <el-form ref="formRef" :model="form" :rules="rules" label-width="90px">
|
|
|
+ <el-form-item label="标题" prop="title">
|
|
|
+ <el-input v-model="form.title" clearable maxlength="80" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="说明" prop="explain">
|
|
|
+ <el-input v-model="form.explain" type="textarea" :rows="4" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="素材" prop="materialText">
|
|
|
+ <el-input v-model="form.materialText" placeholder="多个素材ID用英文逗号分隔,如 11,12,13" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="价格" prop="price">
|
|
|
+ <el-input v-model="form.price" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="客服ID" prop="service_id">
|
|
|
+ <el-input v-model="form.service_id" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="截止时间" prop="endDate">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="form.endDate"
|
|
|
+ type="datetime"
|
|
|
+ value-format="x"
|
|
|
+ placeholder="选择截止时间"
|
|
|
+ style="width: 100%;"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="状态" prop="status">
|
|
|
+ <el-switch
|
|
|
+ v-model="form.status"
|
|
|
+ :active-value="1"
|
|
|
+ :inactive-value="0"
|
|
|
+ active-text="开启"
|
|
|
+ inactive-text="关闭"
|
|
|
+ inline-prompt
|
|
|
+ active-color="#13ce66"
|
|
|
+ inactive-color="#ff4949"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="dialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" :loading="saving" @click="submitForm">保存</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+ </el-main>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+export default {
|
|
|
+ name: "taskList",
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ loading: false,
|
|
|
+ saving: false,
|
|
|
+ list: [],
|
|
|
+ total: 0,
|
|
|
+ query: {
|
|
|
+ page: 1,
|
|
|
+ limit: 15
|
|
|
+ },
|
|
|
+ dialogVisible: false,
|
|
|
+ dialogTitle: "新增任务",
|
|
|
+ form: this.getDefaultForm(),
|
|
|
+ rules: {
|
|
|
+ title: [{ required: true, message: "请输入标题", trigger: "blur" }],
|
|
|
+ explain: [{ required: true, message: "请输入说明", trigger: "blur" }],
|
|
|
+ materialText: [{ required: true, message: "请输入素材ID", trigger: "blur" }],
|
|
|
+ price: [{ required: true, message: "请输入价格", trigger: "blur" }],
|
|
|
+ service_id: [{ required: true, message: "请输入客服ID", trigger: "blur" }],
|
|
|
+ endDate: [{ required: true, message: "请选择截止时间", trigger: "change" }]
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.getList();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ getDefaultForm() {
|
|
|
+ return {
|
|
|
+ id: "",
|
|
|
+ title: "",
|
|
|
+ explain: "",
|
|
|
+ material: [],
|
|
|
+ materialText: "",
|
|
|
+ price: "",
|
|
|
+ service_id: "",
|
|
|
+ endDate: "",
|
|
|
+ end_time: "",
|
|
|
+ status: 1
|
|
|
+ };
|
|
|
+ },
|
|
|
+ formatTime(ts) {
|
|
|
+ if (!ts) return "-";
|
|
|
+ const num = Number(ts);
|
|
|
+ if (!num) return ts;
|
|
|
+ return this.$TOOL.getTime(Math.floor(num / 1000), "YYYY-MM-DD HH:mm:ss");
|
|
|
+ },
|
|
|
+ async getList() {
|
|
|
+ this.loading = true;
|
|
|
+ try {
|
|
|
+ const res = await this.$API.task.list.get(this.query);
|
|
|
+ if (res.code !== 1) {
|
|
|
+ this.$message.error(res.msg || "获取任务列表失败");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const data = res.data || {};
|
|
|
+ const rawList = data.data || data.list || [];
|
|
|
+ this.list = rawList.map((item) => ({
|
|
|
+ ...item,
|
|
|
+ materialDisplay: Array.isArray(item.material) ? item.material : [],
|
|
|
+ // 后端字段为 pirce,这里统一映射给表格展示
|
|
|
+ price: item.price || item.pirce || ""
|
|
|
+ }));
|
|
|
+ this.total = Number(data.count || data.total || this.list.length || 0);
|
|
|
+ } finally {
|
|
|
+ this.loading = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handlePageChange(page) {
|
|
|
+ this.query.page = page;
|
|
|
+ this.getList();
|
|
|
+ },
|
|
|
+ handleSizeChange(limit) {
|
|
|
+ this.query.limit = limit;
|
|
|
+ this.query.page = 1;
|
|
|
+ this.getList();
|
|
|
+ },
|
|
|
+ openCreate() {
|
|
|
+ this.dialogTitle = "新增任务";
|
|
|
+ this.form = this.getDefaultForm();
|
|
|
+ this.dialogVisible = true;
|
|
|
+ },
|
|
|
+ async openEdit(row) {
|
|
|
+ this.dialogTitle = "编辑任务";
|
|
|
+ this.form = this.getDefaultForm();
|
|
|
+ this.dialogVisible = true;
|
|
|
+ const res = await this.$API.task.info.get(row.id);
|
|
|
+ if (res.code !== 1) {
|
|
|
+ this.$message.error(res.msg || "获取任务详情失败");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const info = res.data || {};
|
|
|
+ const material = Array.isArray(info.material) ? info.material : [];
|
|
|
+ const endTime = info.end_time ? Number(info.end_time) * 1000 : "";
|
|
|
+ this.form = {
|
|
|
+ id: info.id || row.id,
|
|
|
+ title: info.title || "",
|
|
|
+ explain: info.explain || "",
|
|
|
+ material,
|
|
|
+ materialText: material.join(","),
|
|
|
+ price: info.price || info.pirce || "",
|
|
|
+ service_id: String(info.service_id || ""),
|
|
|
+ endDate: endTime ? String(endTime) : "",
|
|
|
+ end_time: String(info.end_time || ""),
|
|
|
+ status: Number(info.status ?? 1)
|
|
|
+ };
|
|
|
+ },
|
|
|
+ async submitForm() {
|
|
|
+ this.$refs.formRef.validate(async (valid) => {
|
|
|
+ if (!valid) return false;
|
|
|
+ this.saving = true;
|
|
|
+ try {
|
|
|
+ const material = (this.form.materialText || "")
|
|
|
+ .split(",")
|
|
|
+ .map((item) => item.trim())
|
|
|
+ .filter(Boolean);
|
|
|
+ const endTimeSec = this.form.endDate ? Math.floor(Number(this.form.endDate) / 1000) : "";
|
|
|
+ const payload = {
|
|
|
+ title: this.form.title,
|
|
|
+ explain: this.form.explain,
|
|
|
+ material,
|
|
|
+ pirce: this.form.price,
|
|
|
+ end_time: String(endTimeSec),
|
|
|
+ service_id: this.form.service_id,
|
|
|
+ status: String(this.form.status)
|
|
|
+ };
|
|
|
+ if (this.form.id) payload.id = this.form.id;
|
|
|
+ const res = await this.$API.task.save.post(payload);
|
|
|
+ if (res.code !== 1) {
|
|
|
+ this.$message.error(res.msg || "保存失败");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ this.$message.success(res.msg || "保存成功");
|
|
|
+ this.dialogVisible = false;
|
|
|
+ this.getList();
|
|
|
+ } finally {
|
|
|
+ this.saving = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ async removeTask(row) {
|
|
|
+ const confirm = await this.$confirm("确认删除该任务吗?", "提示", {
|
|
|
+ type: "warning",
|
|
|
+ confirmButtonText: "删除",
|
|
|
+ confirmButtonClass: "el-button--danger"
|
|
|
+ }).catch(() => {});
|
|
|
+ if (confirm !== "confirm") return;
|
|
|
+ const res = await this.$API.task.del.delete(row.id);
|
|
|
+ if (res.code !== 1) {
|
|
|
+ this.$message.error(res.msg || "删除失败");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.$message.success(res.msg || "删除成功");
|
|
|
+ this.getList();
|
|
|
+ },
|
|
|
+ async changeStatus(row, status) {
|
|
|
+ const res = await this.$API.task.setStatus.get(row.id, status);
|
|
|
+ if (res.code !== 1) {
|
|
|
+ this.$message.error(res.msg || "状态更新失败");
|
|
|
+ this.getList();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.$message.success(res.msg || "状态更新成功");
|
|
|
+ row.status = status;
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.header-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+}
|
|
|
+
|
|
|
+.pager {
|
|
|
+ margin-top: 16px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+}
|
|
|
+
|
|
|
+.material-tag {
|
|
|
+ margin-right: 6px;
|
|
|
+ margin-bottom: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+</style>
|