Переглянути джерело

feat: 对接后台任务管理接口并完善任务列表页

doi 3 місяців тому
батько
коміт
95c11281ba

+ 1 - 1
.env.development

@@ -5,7 +5,7 @@ NODE_ENV = development
 VUE_APP_TITLE = 蓝微帧工场
 
 # 接口地址
-VUE_APP_API_BASEURL = http://127.0.0.1:5890/api/
+VUE_APP_API_BASEURL = http://192.168.2.64:5890/adminapi/
 
 # 本地端口
 VUE_APP_PORT = 2808

+ 2 - 2
src/api/model/auth.js

@@ -3,7 +3,7 @@ import http from "@/utils/request"
 
 export default {
 	login: {
-		url: `${config.API_URL}/login/user`,
+		url: `${config.API_URL}/login`,
 		name: "-",
 		post: async function(data={}){
 			return await http.post(this.url, data);
@@ -24,7 +24,7 @@ export default {
 		}
 	},
 	user: {
-		url: `${config.API_URL}/login/profile`,
+		url: `${config.API_URL}/profile`,
 		name: "-",
 		get: async function(data={}){
 			return await http.get(this.url, data);

+ 40 - 0
src/api/model/task.js

@@ -0,0 +1,40 @@
+import config from "@/config";
+import http from "@/utils/request";
+
+export default {
+    list: {
+        url: `${config.API_URL}/task/list`,
+        name: "任务列表",
+        get: async function(params = {}){
+            return await http.get(this.url, params);
+        }
+    },
+    save: {
+        url: `${config.API_URL}/task/save`,
+        name: "任务新增/编辑",
+        post: async function(data = {}){
+            return await http.post(this.url, data);
+        }
+    },
+    del: {
+        url: `${config.API_URL}/task/del`,
+        name: "任务删除",
+        delete: async function(id){
+            return await http.delete(`${this.url}/${id}`);
+        }
+    },
+    setStatus: {
+        url: `${config.API_URL}/task/set_status`,
+        name: "任务状态设置",
+        get: async function(id, status){
+            return await http.get(`${this.url}/${id}/${status}`);
+        }
+    },
+    info: {
+        url: `${config.API_URL}/task/info`,
+        name: "任务详情",
+        get: async function(id){
+            return await http.get(`${this.url}/${id}`);
+        }
+    }
+}

+ 2 - 2
src/config/index.js

@@ -12,7 +12,7 @@ const DEFAULT_CONFIG = {
 	CORE_VER: "1.6.9",
 
 	//接口地址
-	API_URL: process.env.NODE_ENV === 'development' && process.env.VUE_APP_PROXY === 'true' ? "/api" : process.env.VUE_APP_API_BASEURL,
+	API_URL: process.env.NODE_ENV === 'development' && process.env.VUE_APP_PROXY === 'true' ? "/adminapi" : process.env.VUE_APP_API_BASEURL,
 
 	//请求超时
 	TIMEOUT: 10000,
@@ -46,7 +46,7 @@ const DEFAULT_CONFIG = {
 	LANG: 'zh-cn',
 
 	//主题颜色
-	COLOR: '#20222A',
+	COLOR: '',
 
 	//是否加密localStorage, 为空不加密,可填写AES(模式ECB,移位Pkcs7)加密
 	LS_ENCRYPTION: '',

+ 311 - 0
src/views/task/manage/list/index.vue

@@ -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>

+ 2 - 2
vue.config.js

@@ -15,11 +15,11 @@ module.exports = defineConfig({
 		open: false, //运行后自动打开浏览器
 		port: process.env.VUE_APP_PORT, //挂载端口
 		proxy: {
-			'/api': {
+			'/adminapi': {
 				target: process.env.VUE_APP_API_BASEURL,
 				ws: true,
 				pathRewrite: {
-					'^/api': '/'
+					'^/adminapi': '/'
 				}
 			}
 		}