This commit is contained in:
2025-04-24 19:17:53 +08:00
parent 094b560059
commit f9381e74ed
9 changed files with 323 additions and 50 deletions

View File

@@ -391,6 +391,62 @@ const admin = {
data: data data: data
}); });
}, },
getQRList: async (data) => {
return request({
url: '/admin/qrcode/getList',
method: Method.POST,
data: data
});
},
editQRList: async (data) => {
return request({
url: '/admin/qrcode/edit',
method: Method.POST,
data: data
});
},
getArticleCategoryList: async (data) => {
return request({
url: '/admin/articleCategory/getList',
method: Method.POST,
data: data
});
},
addArticleCategory: async (data) => {
return request({
url: '/admin/articleCategory/add',
method: Method.POST,
data: data
});
},
detailArticleCategory: async (data) => {
return request({
url: '/admin/articleCategory/detail',
method: Method.POST,
data: data
});
},
editArticleCategory: async (data) => {
return request({
url: '/admin/articleCategory/edit',
method: Method.POST,
data: data
});
},
delArticle: async (id) => {
return request({
url: '/admin/articleCategory/del',
method: Method.POST,
data: {id}
});
},
weighArticleCategory: async (data) => {
return request({
url: '/admin/articleCategory/weigh',
method: Method.POST,
data: data
});
},
} }
export default admin; export default admin;

View File

@@ -0,0 +1,74 @@
<script setup>
import {ref} from "vue";
import {BASEURL} from "../../utils/request.js";
import {useUserStore} from "../../pinia/UserStore/index.js";
const UserStore = useUserStore();
const file = ref();
const url = defineModel();
const onChange = (_, currentFile) => {
if (currentFile.value.response && currentFile.value.response?.data) url.value = currentFile.value.response?.data;
file.value = {
...currentFile,
// url: URL.createObjectURL(currentFile.file),
};
};
const onProgress = (currentFile) => {
file.value = currentFile;
};
</script>
<template>
{{ url }}
<a-upload
:action="BASEURL + '/admin/upload/upload'"
:fileList="file ? [file] : []"
:show-file-list="false"
:headers="{
'Access-Token': UserStore.token
}"
@change="onChange"
@progress="onProgress"
>
<template #upload-button>
<div
:class="`arco-upload-list-item${
file && file.status === 'error' ? ' arco-upload-list-item-error' : ''
}`"
>
<div
class="arco-upload-list-picture custom-upload-avatar"
v-if="file && file.url"
>
<img :src="file.url"/>
<div class="arco-upload-list-picture-mask">
<IconEdit/>
</div>
<a-progress
v-if="file.status === 'uploading' && file.percent < 100"
:percent="file.percent"
type="circle"
size="mini"
:style="{
position: 'absolute',
left: '50%',
top: '50%',
transform: 'translateX(-50%) translateY(-50%)',
}"
/>
</div>
<div class="arco-upload-picture-card" v-else>
<div class="arco-upload-picture-card-text">
<IconPlus/>
<div style="margin-top: 10px; font-weight: 600">Upload</div>
</div>
</div>
</div>
</template>
</a-upload>
</template>
<style scoped lang="scss">
</style>

View File

@@ -1,6 +1,12 @@
<script setup> <script setup>
import Api from "../../api/index.js"; import Api from "../../api/index.js";
const {size} = defineProps({
size: {
type: String,
default: '100%'
}
});
const emits = defineEmits(['success']); const emits = defineEmits(['success']);
const files = defineModel('file'); const files = defineModel('file');
@@ -14,13 +20,57 @@ const beforeUpload = (file) => {
</script> </script>
<template> <template>
<div class="x-upload-one">
<a-upload draggable @before-upload="beforeUpload"> <a-upload draggable @before-upload="beforeUpload">
<template #upload-button v-if="files"> <template #upload-button v-if="files">
<a-image :src="files"></a-image> <div class="x-upload-one-image">
<a-image :preview="false" :src="files"></a-image>
</div>
</template> </template>
</a-upload> </a-upload>
</div>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
.x-upload-one {
width: v-bind(size);
height: v-bind(size);
:deep(.arco-upload-wrapper) {
width: v-bind(size);
height: v-bind(size);
.x-upload-one-image {
@apply border;
width: v-bind(size);
height: v-bind(size);
display: flex;
align-items: center;
justify-content: center;
}
.arco-upload {
width: v-bind(size);
height: v-bind(size);
.arco-upload-drag {
padding: 0;
width: v-bind(size);
height: v-bind(size);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.arco-upload-drag-text {
padding: 0 5px;
}
svg {
margin-bottom: 0;
}
}
}
}
}
</style> </style>

View File

@@ -1,14 +1,33 @@
<script setup> <script setup>
import {ref} from 'vue'; import {reactive, ref, watch} from 'vue';
import UploadOne from "../../../../../components/upload/UploadOne.vue";
import Api from "../../../../../api/index.js";
import {Message} from "@arco-design/web-vue";
const {title} = defineProps({ const emits = defineEmits(['success']);
title: { const {detail} = defineProps({
type: String, detail: {
default: '二维码' type: Object,
default: {}
} }
}); });
const visible = ref(false); const visible = ref(false);
const form = reactive({
qrcode: null,
position_text: null,
});
watch(
() => visible.value,
(val) => {
if (val) Object.assign(form, detail)
}
)
const edit = async () => {
const {msg} = await Api.admin.editQRList(form);
Message.success(msg);
emits('success');
}
</script> </script>
<template> <template>
@@ -16,15 +35,16 @@ const visible = ref(false);
<a-modal <a-modal
ok-text="提交" ok-text="提交"
:title="title" @ok="edit"
title="二维码"
title-align="start" title-align="start"
v-model:visible="visible"> v-model:visible="visible">
<a-form> <a-form>
<a-form-item label="位置"> <a-form-item label="位置">
<a-input placeholder="首页/添加客服&我的/联系客服"></a-input> <a-input v-model:model-value="form.position_text" disabled></a-input>
</a-form-item> </a-form-item>
<a-form-item label="二维码"> <a-form-item label="二维码">
<a-upload></a-upload> <upload-one size="100px" v-model:file="form.qrcode"></upload-one>
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>

View File

@@ -12,7 +12,7 @@ const columns = [
}, },
{ {
title: '位置', title: '位置',
dataIndex: 'key', dataIndex: 'position_text',
}, },
{ {
title: '二维码', title: '二维码',
@@ -34,9 +34,9 @@ const vo = reactive({
total: 0, total: 0,
}); });
const {loading, pagination, initFetchData} = useTableQuery({ const {loading, pagination, fetchData} = useTableQuery({
parameter: po, parameter: po,
api: Api.system.getData, api: Api.admin.getQRList,
callback: (data) => { callback: (data) => {
Object.assign(vo, data); Object.assign(vo, data);
} }
@@ -52,15 +52,15 @@ const {loading, pagination, initFetchData} = useTableQuery({
:data="vo.rows" :data="vo.rows"
:columns="columns" :columns="columns"
class="flex-grow mt-[20px] w-full"> class="flex-grow mt-[20px] w-full">
<template v-slot:qrcode> <template v-slot:qrcode="{record}">
<a-image <a-image
width="48px" width="48px"
height="48px" height="48px"
src="https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/a8c8cdb109cb051163646151a4a5083b.png~tplv-uwbnlip3yd-webp.webp"> :src="record.qrcode">
</a-image> </a-image>
</template> </template>
<template v-slot:action> <template v-slot:action="{record}">
<EditQR></EditQR> <EditQR :detail="record" @success="fetchData"></EditQR>
</template> </template>
</a-table> </a-table>
</template> </template>

View File

@@ -57,9 +57,9 @@ const vo = reactive({
total: 0, total: 0,
}); });
const {loading, pagination, initFetchData} = useTableQuery({ const {loading, pagination, fetchData} = useTableQuery({
parameter: po, parameter: po,
api: Api.system.getData, api: Api.admin.getArticlevategoryList,
callback: (data) => { callback: (data) => {
Object.assign(vo, data); Object.assign(vo, data);
} }
@@ -67,7 +67,9 @@ const {loading, pagination, initFetchData} = useTableQuery({
</script> </script>
<template> <template>
<AddFrequentlyQuestions></AddFrequentlyQuestions> <AddFrequentlyQuestions
@success="fetchData">
</AddFrequentlyQuestions>
<a-table <a-table
:loading="loading" :loading="loading"

View File

@@ -4,15 +4,16 @@ import {reactive} from "vue";
import useTableQuery from "../../../../../../hooks/useTableQuery.js"; import useTableQuery from "../../../../../../hooks/useTableQuery.js";
import Api from "../../../../../../api/index.js"; import Api from "../../../../../../api/index.js";
import AddSecondaryClassificationManagement from "./components/AddSecondaryClassificationManagement.vue"; import AddSecondaryClassificationManagement from "./components/AddSecondaryClassificationManagement.vue";
import {Message} from "@arco-design/web-vue";
const columns = [ const columns = [
{ {
title: 'ID', title: 'ID',
dataIndex: 'key', dataIndex: 'id',
}, },
{ {
title: '二级分类名称', title: '二级分类名称',
dataIndex: 'key', dataIndex: 'name',
}, },
{ {
title: '排序', title: '排序',
@@ -28,7 +29,8 @@ const columns = [
}, },
]; ];
const po = reactive({}); const po1 = reactive({pid: 1});
const po2 = reactive({pid: 2});
const vo1 = reactive({ const vo1 = reactive({
page: '', page: '',
rows: [], rows: [],
@@ -40,21 +42,28 @@ const vo2 = reactive({
total: 0, total: 0,
}); });
const {loading: loading1, pagination: pagination1, initFetchData: initFetchData1} = useTableQuery({ const {loading: loading1, pagination: pagination1, fetchData: fetchData1} = useTableQuery({
parameter: po, parameter: po1,
api: Api.system.getData, api: Api.admin.getArticleCategoryList,
callback: (data) => { callback: (data) => {
Object.assign(vo1, data); Object.assign(vo1, data);
} }
}); });
const {loading: loading2, pagination: pagination2, initFetchData: initFetchData2} = useTableQuery({ const {loading: loading2, pagination: pagination2, fetchData: fetchData2} = useTableQuery({
parameter: po, parameter: po2,
api: Api.system.getData, api: Api.admin.getArticleCategoryList,
callback: (data) => { callback: (data) => {
Object.assign(vo2, data); Object.assign(vo2, data);
} }
}); });
const del = async (id) => {
const {msg} = await Api.admin.delArticle(id);
Message.success(msg);
await fetchData1();
await fetchData2();
}
</script> </script>
<template> <template>
@@ -63,7 +72,11 @@ const {loading: loading2, pagination: pagination2, initFetchData: initFetchData2
<div class="flex justify-between"> <div class="flex justify-between">
<div class="title">常见问题</div> <div class="title">常见问题</div>
<AddSecondaryClassificationManagement :max-class="1"></AddSecondaryClassificationManagement> <AddSecondaryClassificationManagement
@success="fetchData1"
:pid="1"
:max-class="1">
</AddSecondaryClassificationManagement>
</div> </div>
<a-table <a-table
@@ -73,15 +86,25 @@ const {loading: loading2, pagination: pagination2, initFetchData: initFetchData2
:loading="loading1" :loading="loading1"
:columns="columns" :columns="columns"
class="w-full h-full"> class="w-full h-full">
<template v-slot:sort> <template v-slot:sort="{record}">
<SequenceAdjustment></SequenceAdjustment> <SequenceAdjustment
:id="record.id"
@success="fetchData1"
:api="Api.admin.weighArticleCategory">
</SequenceAdjustment>
</template> </template>
<template v-slot:action> <template v-slot:action="{record}">
<div class="flex gap-[10px]"> <div class="flex gap-[10px]">
<AddSecondaryClassificationManagement :max-class="1"> <AddSecondaryClassificationManagement
@success="fetchData1"
:detail="record"
:pid="1"
:max-class="1">
<a-link :hoverable="false">编辑</a-link> <a-link :hoverable="false">编辑</a-link>
</AddSecondaryClassificationManagement> </AddSecondaryClassificationManagement>
<a-popconfirm content="确定删除吗?" @ok="del(record.id)">
<a-link :hoverable="false" status="danger">删除</a-link> <a-link :hoverable="false" status="danger">删除</a-link>
</a-popconfirm>
</div> </div>
</template> </template>
</a-table> </a-table>
@@ -91,7 +114,11 @@ const {loading: loading2, pagination: pagination2, initFetchData: initFetchData2
<div class="flex justify-between"> <div class="flex justify-between">
<div class="title">基础教学</div> <div class="title">基础教学</div>
<AddSecondaryClassificationManagement :max-class="2"></AddSecondaryClassificationManagement> <AddSecondaryClassificationManagement
@success="fetchData2"
:pid="2"
:max-class="2">
</AddSecondaryClassificationManagement>
</div> </div>
<a-table <a-table
@@ -101,15 +128,25 @@ const {loading: loading2, pagination: pagination2, initFetchData: initFetchData2
:loading="loading2" :loading="loading2"
:columns="columns" :columns="columns"
class="w-full h-full"> class="w-full h-full">
<template v-slot:sort> <template v-slot:sort="{record}">
<SequenceAdjustment></SequenceAdjustment> <SequenceAdjustment
:id="record.id"
@success="fetchData2"
:api="Api.admin.weighArticleCategory">
</SequenceAdjustment>
</template> </template>
<template v-slot:action> <template v-slot:action="{record}">
<div class="flex gap-[10px]"> <div class="flex gap-[10px]">
<AddSecondaryClassificationManagement :max-class="2"> <AddSecondaryClassificationManagement
@success="fetchData2"
:detail="record"
:pid="2"
:max-class="2">
<a-link :hoverable="false">编辑</a-link> <a-link :hoverable="false">编辑</a-link>
</AddSecondaryClassificationManagement> </AddSecondaryClassificationManagement>
<a-popconfirm content="确定删除吗?" @ok="del(record.id)">
<a-link :hoverable="false" status="danger">删除</a-link> <a-link :hoverable="false" status="danger">删除</a-link>
</a-popconfirm>
</div> </div>
</template> </template>
</a-table> </a-table>

View File

@@ -1,20 +1,34 @@
<script setup> <script setup>
import XSelect from "../../../../../../../components/XSelect/index.vue"; import XSelect from "../../../../../../../components/XSelect/index.vue";
import {reactive, ref} from "vue"; import {reactive, ref, watch} from "vue";
import Api from "../../../../../../../api/index.js";
import {Message} from "@arco-design/web-vue";
const {maxClass} = defineProps({ const emits = defineEmits(['success']);
maxClass: { const {maxClass, pid, detail} = defineProps({
pid: {
type: Number, type: Number,
default: 1, default: 1,
},
detail: {
type: Object,
default: {},
} }
}); });
const visible = ref(false); const visible = ref(false);
const form = reactive({ const form = reactive({
title: null, name: null,
}); });
watch(
() => visible.value,
(val) => {
if (val) Object.assign(form, detail);
}
)
const getData = async () => { const getData = async () => {
return new Promise((resolve) => { return new Promise((resolve) => {
resolve({ resolve({
@@ -31,6 +45,23 @@ const getData = async () => {
}); });
}) })
} }
const success = async () => {
if (detail.id) {
const {msg} = await Api.admin.editArticleCategory({
pid,
...form,
});
Message.success(msg);
} else {
const {msg} = await Api.admin.addArticleCategory({
pid,
...form,
});
Message.success(msg);
}
emits('success');
}
</script> </script>
<template> <template>
@@ -46,6 +77,7 @@ const getData = async () => {
<a-modal <a-modal
@ok="success"
title="新增二级分类" title="新增二级分类"
title-align="start" title-align="start"
v-model:visible="visible"> v-model:visible="visible">
@@ -61,7 +93,7 @@ const getData = async () => {
</XSelect> </XSelect>
</a-form-item> </a-form-item>
<a-form-item label="二级分类名称"> <a-form-item label="二级分类名称">
<a-input v-model:model-value="form.title" placeholder="请输入二级分类名称"></a-input> <a-input v-model:model-value="form.name" placeholder="请输入二级分类名称"></a-input>
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>

View File

@@ -3,9 +3,11 @@ import AESCrypto from "./AESCrypto.js";
import {Message} from '@arco-design/web-vue'; import {Message} from '@arco-design/web-vue';
import {useUserStore} from "../pinia/UserStore/index.js"; import {useUserStore} from "../pinia/UserStore/index.js";
export const BASEURL = import.meta.env.MODE === 'development' ? '/baseApi' : import.meta.env.VITE_API_URL;
// 创建 Axios 实例 // 创建 Axios 实例
const request = axios.create({ const request = axios.create({
baseURL: import.meta.env.MODE === 'development' ? '/baseApi' : import.meta.env.VITE_API_URL, // 替换为你的基础 URL baseURL: BASEURL, // 替换为你的基础 URL
timeout: 10000, // 请求超时设置 timeout: 10000, // 请求超时设置
}); });