This commit is contained in:
2025-04-21 10:30:45 +08:00
parent 9731d104f6
commit e89510d7c0
14 changed files with 308 additions and 120 deletions

View File

@@ -29,7 +29,7 @@ const admin = {
method: Method.POST,
});
},
getTaskList: async () => {
getTaskStatusList: async () => {
return request({
url: '/admin/task/getStatusList',
method: Method.POST,
@@ -47,6 +47,81 @@ const admin = {
method: Method.POST,
});
},
getTaskList: async (data) => {
return request({
url: '/admin/task/getTaskList ',
method: Method.POST,
data: data,
});
},
passTask: async (data) => {
return request({
url: '/admin/task/passTask',
method: Method.POST,
data: data,
});
},
refuseTask: async (data) => {
return request({
url: '/admin/task/refuseTask',
method: Method.POST,
data: data,
});
},
stopTask: async (id) => {
return request({
url: '/admin/task/stopTask',
method: Method.POST,
data: {id},
});
},
getTaskDetail: async (id) => {
return request({
url: '/admin/task/getTaskInfo',
method: Method.POST,
data: {id},
});
},
getStatusList: async () => {
return request({
url: '/admin/taskchildren/getStatusList',
method: Method.POST,
});
},
getSubCheckStatusList: async () => {
return request({
url: '/admin/taskchildren/getCheckStatusList',
method: Method.POST,
});
},
getTaskChildrenList: async (data) => {
return request({
url: '/admin/taskchildren/getTaskChildrenList',
method: Method.POST,
data: data
});
},
getTaskChildrenInfo: async (id) => {
return request({
url: '/admin/taskchildren/getTaskChildrenInfo',
method: Method.POST,
data: {id}
});
},
passTaskChildren: async (id) => {
return request({
url: '/admin/taskchildren/passTaskChildren',
method: Method.POST,
data: {id}
});
},
refuseTaskChildren: async (data) => {
return request({
url: '/admin/taskchildren/refuseTaskChildren',
method: Method.POST,
data: data
});
},
}
export default admin;

View File

@@ -48,7 +48,8 @@ const reset = () => {
<a-row :gutter="24">
<template v-for="(item, index) in config" :key="index">
<a-col :span="item.span || 8">
<a-form-item :label="item.label" :class="Math.ceil((index+1) / 3) !== Math.ceil(config.length / 3) ? 'mb20' : ''">
<a-form-item :label="item.label"
:class="Math.ceil((index+1) / 3) !== Math.ceil(config.length / 3) ? 'mb20' : ''">
<template v-if="item.type === FROM_TYPE.INPUT">
<a-input
class="w-full"
@@ -69,7 +70,10 @@ const reset = () => {
<template v-if="item.type === FROM_TYPE.DATETIME">
<a-range-picker
class="w-full"
@change="(v) => from[item.key] = `${v[0]}~${v[1]}`">
@change="(v) => {
from[item.start] = v[0];
from[item.end] = v[1];
}">
</a-range-picker>
</template>
@@ -121,6 +125,7 @@ const reset = () => {
:deep(.arco-form-item) {
margin-bottom: 0;
}
:deep(.arco-form-item-label) {
@apply whitespace-nowrap;
}

View File

@@ -1,5 +1,6 @@
import {Modal, Tag} from "@arco-design/web-vue";
import {Message, Modal, Tag} from "@arco-design/web-vue";
import {h} from 'vue';
import Api from "../../api/index.js";
const ModalContent = {
props: {
@@ -30,12 +31,17 @@ const ModalContent = {
]),
h('div', {class: 'text-[14px] text-[rgb(78, 89, 105)]'}, '无法终止')
]);
case 'none':
return () => h('div', {}, [
h('div', {class: 'text-[14px] text-[rgb(78, 89, 105)]'}, '点击终止子任务后,达人将无法再领取子任务'),
h('div', {class: 'text-[14px] text-[#4E5969]'}, '*请商家及时处理已经被接单的子任务,处理完毕后,剩余任务金额将返款至商家钱包')
]);
}
}
}
const openTerminateTask = (type, status_text = '待上传素材') => {
const status = 'success';
const openTerminateTask = ({type = 'none', status_text = '待上传素材', taskId}) => {
const status = type;
Modal.warning({
title: '确认终止子任务',
@@ -45,7 +51,7 @@ const openTerminateTask = (type, status_text = '待上传素材') => {
h(
ModalContent,
{
status: 'success',
status: status,
status_text: status_text
}
),
@@ -53,8 +59,9 @@ const openTerminateTask = (type, status_text = '待上传素材') => {
status: 'danger',
},
okText: status === 'success' ? '确认终止' : '确认',
onOk: () => {
onOk: async () => {
const {code, msg} = await Api.admin.stopTask(taskId);
if (code === 1) Message.success(msg);
}
});
}

View File

@@ -1,4 +1,4 @@
import {ref, reactive, watch} from 'vue';
import {reactive, ref, watch} from 'vue';
/**
*
@@ -18,7 +18,7 @@ function useTableQuery({
const loading = ref(false);
const pagination = reactive({
current: 1,
page: 1,
pageSize: 20,
total: 0
});
@@ -29,7 +29,7 @@ function useTableQuery({
const params = {
...parameter,
current: pagination.current,
page: pagination.current,
pageSize: pagination.pageSize
}
@@ -40,7 +40,7 @@ function useTableQuery({
callback && callback({
...data,
rows: data.rows.map(v => ({...v, key: v.id})),
rows: data.list.map(v => ({...v, key: v.id})),
});
} finally {
loading.value = false;
@@ -50,6 +50,7 @@ function useTableQuery({
const initFetchData = async () => {
pagination.current = 1;
pagination.total = 0;
await fetchData();
}
watch(

View File

@@ -1,15 +1,40 @@
<script setup>
import {ref} from 'vue';
import {reactive, ref, watch} from 'vue';
import Api from "../../../../../api/index.js";
import {Message} from "@arco-design/web-vue";
const emits = defineEmits(['success']);
const {id} = defineProps({
id: {
type: Number,
default: 0,
}
});
const visible = ref(false);
const detail = reactive({});
const activeKey = ref(1);
const success = () => {
watch(
() => visible.value,
(val) => {
if (val) Api.admin.getTaskChildrenInfo(id).then(({data}) => {
Object.assign(detail, data);
console.log('我看看我看看', data);
});
},
{deep: true}
)
const passTaskChildren = async () => {
const {code, msg} = await Api.admin.passTaskChildren(id);
if (code === 1) Message.success(msg);
emits('success');
}
const refuse = () => {
const refuseTaskChildren = async () => {
const {code, msg} = await Api.admin.refuseTaskChildren(id);
if (code === 1) Message.success(msg);
emits('success');
}
</script>
@@ -20,45 +45,53 @@ const refuse = () => {
title-align="start"
title="预览"
v-model:visible="visible">
<a-tabs v-model:active-key="activeKey" type="rounded">
<a-tab-pane v-for="item in 3" :title="`素材${item}`" :key="item">
<a-tabs v-model:active-key="activeKey" type="rounded" v-if="detail.id !== undefined && detail.id !== null">
<a-tab-pane v-for="(item, index) in detail.content" :title="`素材${index+1}`" :key="index">
<a-form
layout="vertical">
<a-form-item label="标题">
<a-input model-value="终于让我给发现啦!就这个再加上芙贝柔一起绝了"></a-input>
<a-input :model-value="item.title"></a-input>
</a-form-item>
<a-form-item label="正文">
<a-textarea
auto-size
:max-length="1000"
show-word-limit
model-value="终于让我给发现啦!就这个再加上芙贝柔一起绝了终于让我给发现啦!就这个再加上芙贝柔一起绝风格反对赌东道赌东道了终于让我给发现啦!就这个再加上芙贝柔一起绝了就这个再加...">
:model-value="item.content">
</a-textarea>
</a-form-item>
<a-form-item label="话题">
<div id="tag-list" class="w-full bg-[var(--color-neutral-2)] p-[4px]">
<a-tag>#一起变美</a-tag>
<a-tag v-for="v in item.tags_arr">#{{ v }}</a-tag>
</div>
</a-form-item>
<a-form-item label="素材">
<a-image
width="60px"
height="60px"
src="https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/a8c8cdb109cb051163646151a4a5083b.png~tplv-uwbnlip3yd-webp.webp">
</a-image>
<div class="flex flex-wrap gap-[16px]">
<a-image
v-for="v in item.materia_arr"
width="60px"
height="60px"
:src="v">
</a-image>
</div>
</a-form-item>
<a-form-item label="评论区内容">
<div class="flex flex-col gap-[8px] w-full">
<div class="w-full flex gap-[10px] items-center">
1.
<div class="w-full flex gap-[10px] items-center" v-for="v in detail.comment">
{{ v.id }}.
<div class="flex-grow bg-[var(--color-neutral-2)] p-[4px] h-[40px] flex items-center">
<div class="text-[#4E5969] text-[14px] mr-[4px]" v-if="v.pid !== 0">
<icon-redo/>
回复{{ v.pid }}
</div>
<a-image
v-if="v.image"
class="mr-[12px]"
width="30px"
height="30px"
src="https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/a8c8cdb109cb051163646151a4a5083b.png~tplv-uwbnlip3yd-webp.webp">
:src="v.image">
</a-image>
第三个很好用大家可以在网上搜同款
</div>
@@ -71,14 +104,14 @@ const refuse = () => {
<template #footer>
<div class="flex items-center gap-[8px]">
<a-radio-group>
<template v-for="item in 3" :key="item">
<a-radio v-show="activeKey === item" :value="item">选中</a-radio>
<a-checkbox-group>
<template v-for="(item, index) in detail.content" :key="item">
<a-checkbox v-show="activeKey === index" :value="item">选中</a-checkbox>
</template>
</a-radio-group>
</a-checkbox-group>
<a-button @click="success" type="primary" class="ml-auto">通过</a-button>
<a-button @click="refuse">拒绝</a-button>
<a-button @click="passTaskChildren" type="primary" class="ml-auto">通过</a-button>
<a-button @click="refuseTaskChildren">拒绝</a-button>
</div>
</template>
</a-modal>

View File

@@ -1,12 +1,32 @@
<script setup>
import {ref, reactive} from "vue";
import XSelect from '../../../../../components/XSelect/index.vue';
import {reactive, ref} from "vue";
import Api from "../../../../../api/index.js";
import {Message} from "@arco-design/web-vue";
const {id, api} = defineProps({
id: {
type: Number,
default: 0
},
api: {
type: Function,
default: Api.admin.refuseTask
}
});
const emits = defineEmits(['success']);
const visible = ref(false);
const form = reactive({
state: ''
content: ''
});
const success = async () => {
const {code, msg} = await api({
id: id,
...form
});
if (code === 1) Message.success(msg);
emits('success');
}
</script>
<template>
@@ -15,10 +35,11 @@ const form = reactive({
<a-modal
title-align="start"
title="任务审核拒绝"
@ok="success"
v-model:visible="visible">
<a-form :model="form">
<a-form-item label="拒绝原因">
<XSelect :api="Api.system.getSelect" placeholder="请输入拒绝原因"></XSelect>
<a-input v-model:model-value="form.content" placeholder="请输入拒绝原因"></a-input>
</a-form-item>
</a-form>
</a-modal>

View File

@@ -1,30 +1,64 @@
<script setup>
import XSelect from "../../../../../components/XSelect/index.vue";
import {ref, reactive} from "vue";
import {reactive, ref, watch} from "vue";
import Api from "../../../../../api/index.js";
import {Message} from "@arco-design/web-vue";
const {id} = defineProps({
id: {
type: Number,
default: 0
}
});
const emits = defineEmits(['success']);
const visible = ref(false);
const form = reactive({
name: null,
id: null,
real_price: null,
security_level: null,
time_level: null,
});
watch(
() => visible.value,
(val) => {
if (val) {
Api.admin.getTaskDetail(id).then(({data}) => {
form.id = data.id;
form.real_price = data.real_price;
form.security_level = data.security_level;
form.time_level = data.time_level;
});
}
},
{
deep: true,
}
)
const success = async () => {
const {msg, code} = await Api.admin.passTask(form);
if (code === 1) Message.success(msg);
emits('success');
}
</script>
<template>
<a-link :hoverable="false" status="success" @click="visible=true">通过</a-link>
<a-modal
@ok="success"
title-align="start"
title="任务审核通过"
v-model:visible="visible">
<a-form :model="form" layout="vertical">
<a-form-item label="该任务对达人端的展示价格为:">
<XSelect :api="Api.system.getSelect"></XSelect>
<a-input v-model:model-value="form.real_price" placeholder="请输入价格"></a-input>
</a-form-item>
<a-form-item label="安全评分">
<XSelect :api="Api.system.getSelect" placeholder="请输入安全评分满分100"></XSelect>
<a-input v-model:model-value="form.security_level" placeholder="请输入安全评分满分100"></a-input>
</a-form-item>
<a-form-item label="耗时评分">
<XSelect :api="Api.system.getSelect" placeholder="请输入耗时评分满分100"></XSelect>
<a-input v-model:model-value="form.time_level" placeholder="请输入耗时评分满分100"></a-input>
</a-form-item>
</a-form>
</a-modal>

View File

@@ -1,29 +1,29 @@
<script setup>
import {reactive, ref} from "vue";
import {reactive} from "vue";
import Filter from "../../../../components/Filter/index.vue";
import useTableQuery from "../../../../hooks/useTableQuery.js";
import Api from "../../../../api/index.js";
import RejectTaskModal from "./components/RejectTaskModal.vue";
import TaskPassedReviewModal from "./components/TaskPassedReviewModal.vue";
import PreviewTaskModal from "./components/PreviewTaskModal.vue";
import openTerminateTask from "../../../../components/TerminateTask/TerminateTask.js";
import {Message} from "@arco-design/web-vue";
import RejectTaskModal from "./components/RejectTaskModal.vue";
const columns = [
{
title: '任务编号',
dataIndex: 'key',
dataIndex: 'code',
},
{
title: '子任务编号',
dataIndex: 'key',
dataIndex: 'uid',
},
{
title: '子任务状态',
dataIndex: 'key',
dataIndex: 'status_text',
},
{
title: '平台审核状态',
dataIndex: 'key',
dataIndex: 'check_status_text',
},
{
title: '操作',
@@ -50,44 +50,18 @@ const FilterConfig = [
type: 'select',
label: '平台审核状态',
placeholder: '请选择平台审核状态',
api: async () => ({
data: [
{
name: '选项一',
id: 1,
},
{
name: '选项二',
id: 2,
},
{
name: '选项三',
id: 3,
},
]
}),
api: async () => {
return await Api.admin.getSubCheckStatusList();
},
},
{
key: 'wd',
type: 'select',
label: '子任务状态',
placeholder: '请选择子任务状态',
api: async () => ({
data: [
{
name: '选项一',
id: 1,
},
{
name: '选项二',
id: 2,
},
{
name: '选项三',
id: 3,
},
]
}),
api: async () => {
return await Api.admin.getStatusList();
},
},
{
key: 'wd',
@@ -106,14 +80,26 @@ const vo = reactive({
total: 0,
});
const {loading, pagination, initFetchData} = useTableQuery({
const {loading, pagination, initFetchData, fetchData} = useTableQuery({
parameter: po,
api: Api.system.getData,
api: Api.admin.getTaskChildrenList,
callback: (data) => {
Object.assign(vo, data);
console.log(vo);
}
});
const passTaskChildren = async (id) => {
const {code, msg} = await Api.admin.passTaskChildren(id);
if (code === 1) Message.success(msg);
await fetchData();
}
const refuseTaskChildren = async (id) => {
const {code, msg} = await Api.admin.refuseTaskChildren(id);
if (code === 1) Message.success(msg);
await fetchData();
}
</script>
<template>
@@ -121,6 +107,7 @@ const {loading, pagination, initFetchData} = useTableQuery({
<a-card>
<Filter
v-model:from="po"
@search="initFetchData"
:config="FilterConfig">
</Filter>
@@ -131,11 +118,17 @@ const {loading, pagination, initFetchData} = useTableQuery({
:loading="loading"
:columns="columns"
class="flex-grow">
<template v-slot:action>
<template v-slot:action="{record}">
<div class="flex items-center gap-[20px] justify-between">
<PreviewTaskModal></PreviewTaskModal>
<TaskPassedReviewModal></TaskPassedReviewModal>
<RejectTaskModal></RejectTaskModal>
<PreviewTaskModal :id="record.id" @success="fetchData"></PreviewTaskModal>
<a-popconfirm content="确定通过?" @ok="passTaskChildren(record.id)">
<a-link :hoverable="false" status="success">通过</a-link>
</a-popconfirm>
<RejectTaskModal
:api="Api.admin.refuseTaskChildren"
:id="record.id"
@success="fetchData">
</RejectTaskModal>
<a-link :hoverable="false" status="danger" @click="openTerminateTask">终止子任务</a-link>
</div>
</template>

View File

@@ -10,43 +10,43 @@ import TerminateTask from "../../../../components/TerminateTask/TerminateTask.js
const columns = [
{
title: '任务编号',
dataIndex: 'key',
dataIndex: 'code',
},
{
title: '任务名称',
dataIndex: 'key',
dataIndex: 'goods_name',
},
{
title: '发布渠道',
dataIndex: 'key',
dataIndex: 'platform_name',
},
{
title: '创建时间',
dataIndex: 'key',
dataIndex: 'createtime',
},
{
title: '子任务数',
dataIndex: 'key',
dataIndex: 'children_num',
},
{
title: '子任务报价',
dataIndex: 'key',
dataIndex: 'children_price',
},
{
title: '商家ID',
dataIndex: 'key',
dataIndex: 'b_uid',
},
{
title: '平台报价',
dataIndex: 'key',
dataIndex: 'real_price',
},
{
title: '平台审核状态',
dataIndex: 'key',
dataIndex: 'check_status_text',
},
{
title: '商家任务状态',
dataIndex: 'key',
dataIndex: 'status_text',
},
{
title: '操作',
@@ -57,19 +57,19 @@ const columns = [
];
const FilterConfig = [
{
key: 'wd',
key: 'uid',
type: 'input',
label: '商家ID',
placeholder: '请输入商家ID',
},
{
key: 'wd',
key: 'name',
type: 'input',
label: '任务名称',
placeholder: '请输入任务名称',
},
{
key: 'wd',
key: 'platform',
type: 'select',
label: '任务渠道',
placeholder: '请选择任务渠道',
@@ -78,21 +78,23 @@ const FilterConfig = [
},
},
{
key: 'wd',
key: 'status',
type: 'select',
label: '任务状态',
placeholder: '请选择任务状态',
api: async () => {
return await Api.admin.getTaskList();
return await Api.admin.getTaskStatusList();
},
},
{
key: 'wd',
key: 'datetime',
start: 'start_time',
end: 'end_time',
type: 'datetime',
label: '创建时间',
},
{
key: 'wd',
key: 'check_status',
type: 'select',
label: '审核状态',
placeholder: '请选择审核状态',
@@ -103,7 +105,13 @@ const FilterConfig = [
];
const po = reactive({
wd: null,
uid: null,
status: null,
check_status: null,
platform: null,
name: null,
start_time: null,
end_time: null,
});
const vo = reactive({
page: '',
@@ -111,12 +119,11 @@ const vo = reactive({
total: 0,
});
const {loading, pagination, initFetchData} = useTableQuery({
const {loading, pagination, initFetchData, fetchData} = useTableQuery({
parameter: po,
api: Api.system.getData,
api: Api.admin.getTaskList,
callback: (data) => {
Object.assign(vo, data);
console.log(vo);
}
});
</script>
@@ -125,6 +132,7 @@ const {loading, pagination, initFetchData} = useTableQuery({
<!-- 任务审核 -->
<a-card>
<Filter
@search="initFetchData"
v-model:from="po"
:config="FilterConfig">
</Filter>
@@ -136,12 +144,14 @@ const {loading, pagination, initFetchData} = useTableQuery({
:loading="loading"
:columns="columns"
class="flex-grow">
<template v-slot:action>
<template v-slot:action="{record}">
<div class="flex items-center gap-[20px]">
<a-link :hoverable="false">编辑</a-link>
<TaskPassedReviewModal></TaskPassedReviewModal>
<RejectTaskModal></RejectTaskModal>
<a-link :hoverable="false" status="danger" class="ml-auto" @click="TerminateTask">终止</a-link>
<TaskPassedReviewModal :id="record.id" @success="fetchData"></TaskPassedReviewModal>
<RejectTaskModal :id="record.id" @success="fetchData"></RejectTaskModal>
<a-link :hoverable="false" status="danger" class="ml-auto"
@click="TerminateTask({taskId: record.id})">终止
</a-link>
</div>
</template>
</a-table>

View File

@@ -42,6 +42,7 @@ export const useSystemStore = defineStore("SystemStore", () => {
redirect: `/home/${routes[0].path}`,
children: []
});
isRoot.value = false;
await router.replace(router.currentRoute.value.fullPath);
}

View File

@@ -36,6 +36,8 @@ export const useUserStore = defineStore("UserStore", () => {
const logout = async () => {
isLogin.value = false;
userInfo.value = null;
token.value = null;
const {clearRouter} = useSystemStore();
await clearRouter();
}

View File

@@ -37,15 +37,17 @@ body {
.arco-table-container {
@apply h-full;
.arco-table-element {
@apply min-h-full;
//@apply min-h-full;
}
}
}
}
}
.arco-table-th-title {
@apply whitespace-nowrap flex-shrink-0;
}
.mock-card {
@apply p-[20px] bg-[#fff];
border: 1px solid var(--color-neutral-3);

View File

@@ -5,7 +5,7 @@ import {useUserStore} from "../pinia/UserStore/index.js";
// 创建 Axios 实例
const request = axios.create({
baseURL: import.meta.env.MODE === 'development' ? '/api' : import.meta.env.VITE_API_URL, // 替换为你的基础 URL
baseURL: import.meta.env.MODE === 'development' ? '/baseApi' : import.meta.env.VITE_API_URL, // 替换为你的基础 URL
timeout: 10000, // 请求超时设置
});
@@ -35,6 +35,10 @@ request.interceptors.request.use((config) => {
request.interceptors.response.use((response) => {
const {data: {msg, code, data}, config: {url}} = response;
console.log(response)
if (code === 401) {
const {logout} = useUserStore();
logout();
}
if (code !== 1) {
Message.error(msg);
}