Compare commits

...

8 Commits
main ... dev1.0

23 changed files with 912 additions and 537 deletions

4
.gitignore vendored
View File

@ -36,3 +36,7 @@ components.d.ts
yarn.lock
package-lock.json
public
*.zip
*.rar

View File

@ -1,71 +1,68 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>粤好生活-后台管理系统</title>
<style>
* {
margin: 0;
padding: 0;
}
.preload {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
.circular {
height: 42px;
width: 42px;
animation: loading-rotate 2s linear infinite;
}
.circular .path {
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90, 150;
stroke-dashoffset: 0;
stroke-width: 2;
stroke: #4073fa;
stroke-linecap: round;
}
@keyframes loading-rotate {
100% {
transform: rotate(1turn);
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>思缘生活-后台管理系统</title>
<style>
* {
margin: 0;
padding: 0;
}
}
@keyframes loading-dash {
.preload {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
0% {
.circular {
height: 42px;
width: 42px;
animation: loading-rotate 2s linear infinite;
}
.circular .path {
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
stroke-dashoffset: 0;
stroke-width: 2;
stroke: #4073fa;
stroke-linecap: round;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
@keyframes loading-rotate {
100% {
transform: rotate(1turn);
}
}
}
</style>
<script src="https://map.qq.com/api/gljs?libraries=tools&v=1.exp&key=2SABZ-S4TWH-AMCDO-W742B-SKEOE-UWBKJ"></script>
</head>
<body>
<div id="app">
<div class="preload">
<svg viewBox="25 25 50 50" class="circular">
<circle cx="50" cy="50" r="20" fill="none" class="path"></circle>
</svg>
@keyframes loading-dash {
0% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
</style>
<script src="https://map.qq.com/api/gljs?libraries=tools&v=1.exp&key=2SABZ-S4TWH-AMCDO-W742B-SKEOE-UWBKJ"></script>
</head>
<body>
<div id="app">
<div class="preload">
<svg viewBox="25 25 50 50" class="circular">
<circle cx="50" cy="50" r="20" fill="none" class="path"></circle>
</svg>
</div>
</div>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@ -1,130 +1,125 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>粤好生活-后台管理系统</title>
<style>
* {
margin: 0;
padding: 0;
}
.preload {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
.circular {
height: 42px;
width: 42px;
-webkit-animation: loading-rotate 2s linear infinite;
animation: loading-rotate 2s linear infinite;
}
.circular .path {
-webkit-animation: loading-dash 1.5s ease-in-out infinite;
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90, 150;
stroke-dashoffset: 0;
stroke-width: 2;
stroke: #4073fa;
stroke-linecap: round;
}
@-webkit-keyframes loading-rotate {
100% {
transform: rotate(1turn);
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>思缘生活-后台管理系统</title>
<style>
* {
margin: 0;
padding: 0;
}
}
@keyframes loading-rotate {
100% {
transform: rotate(1turn);
.preload {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
}
@-webkit-keyframes loading-dash {
.circular {
height: 42px;
width: 42px;
-webkit-animation: loading-rotate 2s linear infinite;
animation: loading-rotate 2s linear infinite;
}
0% {
.circular .path {
-webkit-animation: loading-dash 1.5s ease-in-out infinite;
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
stroke-dashoffset: 0;
stroke-width: 2;
stroke: #4073fa;
stroke-linecap: round;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
@keyframes loading-dash {
0% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
@-webkit-keyframes loading-rotate {
100% {
transform: rotate(1turn);
}
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
@keyframes loading-rotate {
100% {
transform: rotate(1turn);
}
}
}
</style>
<script src="https://map.qq.com/api/gljs?libraries=tools&v=1.exp&key=2SABZ-S4TWH-AMCDO-W742B-SKEOE-UWBKJ"></script>
<script type="module" crossorigin src="/assets/index.1f6a65da.js"></script>
<link rel="modulepreload" crossorigin href="/assets/@vue.ad3a2c51.js">
<link rel="modulepreload" crossorigin href="/assets/@vueuse.c2fd8b33.js">
<link rel="modulepreload" crossorigin href="/assets/@element-plus.4b8482d8.js">
<link rel="modulepreload" crossorigin href="/assets/lodash-es.61686ec6.js">
<link rel="modulepreload" crossorigin href="/assets/axios.136fb7b6.js">
<link rel="modulepreload" crossorigin href="/assets/dayjs.c8b8967e.js">
<link rel="modulepreload" crossorigin href="/assets/async-validator.fb49d0f5.js">
<link rel="modulepreload" crossorigin href="/assets/@ctrl.82a509e0.js">
<link rel="modulepreload" crossorigin href="/assets/@popperjs.36402333.js">
<link rel="modulepreload" crossorigin href="/assets/escape-html.e5dfadb9.js">
<link rel="modulepreload" crossorigin href="/assets/normalize-wheel-es.8aeb3683.js">
<link rel="modulepreload" crossorigin href="/assets/memoize-one.4ee5c96d.js">
<link rel="modulepreload" crossorigin href="/assets/element-plus.02c000b8.js">
<link rel="modulepreload" crossorigin href="/assets/lodash.8effadcb.js">
<link rel="modulepreload" crossorigin href="/assets/vue-router.93f65f3b.js">
<link rel="modulepreload" crossorigin href="/assets/vue-demi.ebc8116b.js">
<link rel="modulepreload" crossorigin href="/assets/pinia.f0255b9b.js">
<link rel="modulepreload" crossorigin href="/assets/clone.4b381e37.js">
<link rel="modulepreload" crossorigin href="/assets/color-name.e7a4e1d3.js">
<link rel="modulepreload" crossorigin href="/assets/color-convert.755d189f.js">
<link rel="modulepreload" crossorigin href="/assets/color-string.e356f5de.js">
<link rel="modulepreload" crossorigin href="/assets/color.0adfd97a.js">
<link rel="modulepreload" crossorigin href="/assets/css-color-function.3cb93b94.js">
<link rel="modulepreload" crossorigin href="/assets/nprogress.09754c1e.js">
<link rel="modulepreload" crossorigin href="/assets/clipboard.42524a75.js">
<link rel="modulepreload" crossorigin href="/assets/vue-clipboard3.c5f2d3ed.js">
<link rel="modulepreload" crossorigin href="/assets/tslib.60310f1a.js">
<link rel="modulepreload" crossorigin href="/assets/zrender.8ee1a698.js">
<link rel="modulepreload" crossorigin href="/assets/echarts.234b3572.js">
<link rel="modulepreload" crossorigin href="/assets/highlight.js.4ebdf9a4.js">
<link rel="modulepreload" crossorigin href="/assets/@highlightjs.0d5173e3.js">
<link rel="modulepreload" crossorigin href="/assets/mitt.d8e3ba72.js">
<link rel="modulepreload" crossorigin href="/assets/vue3-eventbus.c7305b83.js">
<link rel="stylesheet" href="/assets/element-plus.149e8a96.css">
<link rel="stylesheet" href="/assets/nprogress.a2a0c377.css">
<link rel="stylesheet" href="/assets/highlight.5f5db245.css">
<link rel="stylesheet" href="/assets/index.8c422639.css">
</head>
<body>
<div id="app">
<div class="preload">
<svg viewBox="25 25 50 50" class="circular">
<circle cx="50" cy="50" r="20" fill="none" class="path"></circle>
</svg>
@-webkit-keyframes loading-dash {
0% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
@keyframes loading-dash {
0% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
</style>
<script src="https://map.qq.com/api/gljs?libraries=tools&v=1.exp&key=2SABZ-S4TWH-AMCDO-W742B-SKEOE-UWBKJ"></script>
<script type="module" crossorigin src="/assets/index.073f879d.js"></script>
<link rel="modulepreload" crossorigin href="/assets/@vue.ad3a2c51.js" />
<link rel="modulepreload" crossorigin href="/assets/@vueuse.c2fd8b33.js" />
<link rel="modulepreload" crossorigin href="/assets/@element-plus.4b8482d8.js" />
<link rel="modulepreload" crossorigin href="/assets/lodash-es.61686ec6.js" />
<link rel="modulepreload" crossorigin href="/assets/axios.136fb7b6.js" />
<link rel="modulepreload" crossorigin href="/assets/dayjs.c8b8967e.js" />
<link rel="modulepreload" crossorigin href="/assets/async-validator.fb49d0f5.js" />
<link rel="modulepreload" crossorigin href="/assets/@ctrl.82a509e0.js" />
<link rel="modulepreload" crossorigin href="/assets/@popperjs.36402333.js" />
<link rel="modulepreload" crossorigin href="/assets/escape-html.e5dfadb9.js" />
<link rel="modulepreload" crossorigin href="/assets/normalize-wheel-es.8aeb3683.js" />
<link rel="modulepreload" crossorigin href="/assets/memoize-one.4ee5c96d.js" />
<link rel="modulepreload" crossorigin href="/assets/element-plus.02c000b8.js" />
<link rel="modulepreload" crossorigin href="/assets/lodash.8effadcb.js" />
<link rel="modulepreload" crossorigin href="/assets/vue-router.93f65f3b.js" />
<link rel="modulepreload" crossorigin href="/assets/vue-demi.ebc8116b.js" />
<link rel="modulepreload" crossorigin href="/assets/pinia.f0255b9b.js" />
<link rel="modulepreload" crossorigin href="/assets/clone.4b381e37.js" />
<link rel="modulepreload" crossorigin href="/assets/color-name.e7a4e1d3.js" />
<link rel="modulepreload" crossorigin href="/assets/color-convert.755d189f.js" />
<link rel="modulepreload" crossorigin href="/assets/color-string.e356f5de.js" />
<link rel="modulepreload" crossorigin href="/assets/color.0adfd97a.js" />
<link rel="modulepreload" crossorigin href="/assets/css-color-function.3cb93b94.js" />
<link rel="modulepreload" crossorigin href="/assets/nprogress.09754c1e.js" />
<link rel="modulepreload" crossorigin href="/assets/clipboard.42524a75.js" />
<link rel="modulepreload" crossorigin href="/assets/vue-clipboard3.c5f2d3ed.js" />
<link rel="modulepreload" crossorigin href="/assets/tslib.60310f1a.js" />
<link rel="modulepreload" crossorigin href="/assets/zrender.8ee1a698.js" />
<link rel="modulepreload" crossorigin href="/assets/echarts.234b3572.js" />
<link rel="modulepreload" crossorigin href="/assets/highlight.js.4ebdf9a4.js" />
<link rel="modulepreload" crossorigin href="/assets/@highlightjs.0d5173e3.js" />
<link rel="modulepreload" crossorigin href="/assets/mitt.d8e3ba72.js" />
<link rel="modulepreload" crossorigin href="/assets/vue3-eventbus.c7305b83.js" />
<link rel="stylesheet" href="/assets/element-plus.149e8a96.css" />
<link rel="stylesheet" href="/assets/nprogress.a2a0c377.css" />
<link rel="stylesheet" href="/assets/highlight.5f5db245.css" />
<link rel="stylesheet" href="/assets/index.8c422639.css" />
</head>
<body>
<div id="app">
<div class="preload">
<svg viewBox="25 25 50 50" class="circular">
<circle cx="50" cy="50" r="20" fill="none" class="path"></circle>
</svg>
</div>
</div>
</div>
</body>
</html>
</body>
</html>

View File

@ -1,130 +1,126 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>粤好生活-后台管理系统</title>
<style>
* {
margin: 0;
padding: 0;
}
.preload {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
.circular {
height: 42px;
width: 42px;
-webkit-animation: loading-rotate 2s linear infinite;
animation: loading-rotate 2s linear infinite;
}
.circular .path {
-webkit-animation: loading-dash 1.5s ease-in-out infinite;
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90, 150;
stroke-dashoffset: 0;
stroke-width: 2;
stroke: #4073fa;
stroke-linecap: round;
}
@-webkit-keyframes loading-rotate {
100% {
transform: rotate(1turn);
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>思缘生活-后台管理系统</title>
<style>
* {
margin: 0;
padding: 0;
}
}
@keyframes loading-rotate {
100% {
transform: rotate(1turn);
.preload {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
}
@-webkit-keyframes loading-dash {
.circular {
height: 42px;
width: 42px;
-webkit-animation: loading-rotate 2s linear infinite;
animation: loading-rotate 2s linear infinite;
}
0% {
.circular .path {
-webkit-animation: loading-dash 1.5s ease-in-out infinite;
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
stroke-dashoffset: 0;
stroke-width: 2;
stroke: #4073fa;
stroke-linecap: round;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
@keyframes loading-dash {
0% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
@-webkit-keyframes loading-rotate {
100% {
transform: rotate(1turn);
}
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
@keyframes loading-rotate {
100% {
transform: rotate(1turn);
}
}
}
</style>
<script src="https://map.qq.com/api/gljs?libraries=tools&v=1.exp&key=2SABZ-S4TWH-AMCDO-W742B-SKEOE-UWBKJ"></script>
<script type="module" crossorigin src="/assets/index.7e71cca7.js"></script>
<link rel="modulepreload" crossorigin href="/assets/@vue.ad3a2c51.js">
<link rel="modulepreload" crossorigin href="/assets/@vueuse.c2fd8b33.js">
<link rel="modulepreload" crossorigin href="/assets/@element-plus.4b8482d8.js">
<link rel="modulepreload" crossorigin href="/assets/lodash-es.61686ec6.js">
<link rel="modulepreload" crossorigin href="/assets/axios.136fb7b6.js">
<link rel="modulepreload" crossorigin href="/assets/dayjs.c8b8967e.js">
<link rel="modulepreload" crossorigin href="/assets/async-validator.fb49d0f5.js">
<link rel="modulepreload" crossorigin href="/assets/@ctrl.82a509e0.js">
<link rel="modulepreload" crossorigin href="/assets/@popperjs.36402333.js">
<link rel="modulepreload" crossorigin href="/assets/escape-html.e5dfadb9.js">
<link rel="modulepreload" crossorigin href="/assets/normalize-wheel-es.8aeb3683.js">
<link rel="modulepreload" crossorigin href="/assets/memoize-one.4ee5c96d.js">
<link rel="modulepreload" crossorigin href="/assets/element-plus.02c000b8.js">
<link rel="modulepreload" crossorigin href="/assets/lodash.8effadcb.js">
<link rel="modulepreload" crossorigin href="/assets/vue-router.93f65f3b.js">
<link rel="modulepreload" crossorigin href="/assets/vue-demi.ebc8116b.js">
<link rel="modulepreload" crossorigin href="/assets/pinia.f0255b9b.js">
<link rel="modulepreload" crossorigin href="/assets/clone.4b381e37.js">
<link rel="modulepreload" crossorigin href="/assets/color-name.e7a4e1d3.js">
<link rel="modulepreload" crossorigin href="/assets/color-convert.755d189f.js">
<link rel="modulepreload" crossorigin href="/assets/color-string.e356f5de.js">
<link rel="modulepreload" crossorigin href="/assets/color.0adfd97a.js">
<link rel="modulepreload" crossorigin href="/assets/css-color-function.3cb93b94.js">
<link rel="modulepreload" crossorigin href="/assets/nprogress.09754c1e.js">
<link rel="modulepreload" crossorigin href="/assets/clipboard.42524a75.js">
<link rel="modulepreload" crossorigin href="/assets/vue-clipboard3.c5f2d3ed.js">
<link rel="modulepreload" crossorigin href="/assets/tslib.60310f1a.js">
<link rel="modulepreload" crossorigin href="/assets/zrender.8ee1a698.js">
<link rel="modulepreload" crossorigin href="/assets/echarts.234b3572.js">
<link rel="modulepreload" crossorigin href="/assets/highlight.js.4ebdf9a4.js">
<link rel="modulepreload" crossorigin href="/assets/@highlightjs.0d5173e3.js">
<link rel="modulepreload" crossorigin href="/assets/mitt.d8e3ba72.js">
<link rel="modulepreload" crossorigin href="/assets/vue3-eventbus.c7305b83.js">
<link rel="stylesheet" href="/assets/element-plus.149e8a96.css">
<link rel="stylesheet" href="/assets/nprogress.a2a0c377.css">
<link rel="stylesheet" href="/assets/highlight.5f5db245.css">
<link rel="stylesheet" href="/assets/index.8c422639.css">
</head>
<body>
<div id="app">
<div class="preload">
<svg viewBox="25 25 50 50" class="circular">
<circle cx="50" cy="50" r="20" fill="none" class="path"></circle>
</svg>
@-webkit-keyframes loading-dash {
0% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
@keyframes loading-dash {
0% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
</style>
<script src="https://map.qq.com/api/gljs?libraries=tools&v=1.exp&key=2SABZ-S4TWH-AMCDO-W742B-SKEOE-UWBKJ"></script>
<script type="module" crossorigin src="/assets/index.2c7b212c.js"></script>
<link rel="modulepreload" crossorigin href="/assets/@vue.ad3a2c51.js">
<link rel="modulepreload" crossorigin href="/assets/@vueuse.c2fd8b33.js">
<link rel="modulepreload" crossorigin href="/assets/@element-plus.4b8482d8.js">
<link rel="modulepreload" crossorigin href="/assets/lodash-es.61686ec6.js">
<link rel="modulepreload" crossorigin href="/assets/axios.136fb7b6.js">
<link rel="modulepreload" crossorigin href="/assets/dayjs.c8b8967e.js">
<link rel="modulepreload" crossorigin href="/assets/async-validator.fb49d0f5.js">
<link rel="modulepreload" crossorigin href="/assets/@ctrl.82a509e0.js">
<link rel="modulepreload" crossorigin href="/assets/@popperjs.36402333.js">
<link rel="modulepreload" crossorigin href="/assets/escape-html.e5dfadb9.js">
<link rel="modulepreload" crossorigin href="/assets/normalize-wheel-es.8aeb3683.js">
<link rel="modulepreload" crossorigin href="/assets/memoize-one.4ee5c96d.js">
<link rel="modulepreload" crossorigin href="/assets/element-plus.02c000b8.js">
<link rel="modulepreload" crossorigin href="/assets/lodash.8effadcb.js">
<link rel="modulepreload" crossorigin href="/assets/vue-router.93f65f3b.js">
<link rel="modulepreload" crossorigin href="/assets/vue-demi.ebc8116b.js">
<link rel="modulepreload" crossorigin href="/assets/pinia.f0255b9b.js">
<link rel="modulepreload" crossorigin href="/assets/clone.4b381e37.js">
<link rel="modulepreload" crossorigin href="/assets/color-name.e7a4e1d3.js">
<link rel="modulepreload" crossorigin href="/assets/color-convert.755d189f.js">
<link rel="modulepreload" crossorigin href="/assets/color-string.e356f5de.js">
<link rel="modulepreload" crossorigin href="/assets/color.0adfd97a.js">
<link rel="modulepreload" crossorigin href="/assets/css-color-function.3cb93b94.js">
<link rel="modulepreload" crossorigin href="/assets/nprogress.09754c1e.js">
<link rel="modulepreload" crossorigin href="/assets/clipboard.42524a75.js">
<link rel="modulepreload" crossorigin href="/assets/vue-clipboard3.c5f2d3ed.js">
<link rel="modulepreload" crossorigin href="/assets/tslib.60310f1a.js">
<link rel="modulepreload" crossorigin href="/assets/zrender.8ee1a698.js">
<link rel="modulepreload" crossorigin href="/assets/echarts.234b3572.js">
<link rel="modulepreload" crossorigin href="/assets/highlight.js.4ebdf9a4.js">
<link rel="modulepreload" crossorigin href="/assets/@highlightjs.0d5173e3.js">
<link rel="modulepreload" crossorigin href="/assets/mitt.d8e3ba72.js">
<link rel="modulepreload" crossorigin href="/assets/vue3-eventbus.c7305b83.js">
<link rel="stylesheet" href="/assets/element-plus.149e8a96.css">
<link rel="stylesheet" href="/assets/nprogress.a2a0c377.css">
<link rel="stylesheet" href="/assets/highlight.5f5db245.css">
<link rel="stylesheet" href="/assets/index.8c422639.css">
</head>
<body>
<div id="app">
<div class="preload">
<svg viewBox="25 25 50 50" class="circular">
<circle cx="50" cy="50" r="20" fill="none" class="path"></circle>
</svg>
</div>
</div>
</div>
</body>
</html>
</body>
</html>

View File

@ -0,0 +1,20 @@
import request from '@/utils/request'
export function addOperation(params: any) {
return request.post({ url: '/customerService/add', params })
}
export function editOperation(params: any) {
return request.post({ url: '/customerService/edit', params })
}
export function operationDetail(params: any) {
return request.get({ url: '/customerService/detail', params })
}
export function getOperationList() {
return request.get({ url: '/customerService/list' })
}
export function delOperation(params: any) {
return request.post({ url: '/customerService/del', params })
}
export function OperationStatus(params: any) {
return request.post({ url: '/customerService/enable', params })
}

View File

@ -8,7 +8,7 @@
*/
const config = {
terminal: 1, //终端
title: '粤好生活-后台管理系统', //网站默认标题
title: '思缘生活-后台管理系统', //网站默认标题
version: '1.3.3', //版本号
baseUrl: `${import.meta.env.VITE_APP_BASE_URL || ''}/`, //请求接口域名
urlPrefix: 'api', //请求默认前缀

View File

@ -181,4 +181,8 @@ export const StaffRefundEnumMap: Record<number, string> = {
export enum RefundEnum {
SETTING = 1,
UN_SETTING = 0
}
}
export enum PriceEnum {
CUSTOMER_PRICE = 0, //固定价格
TEACHER_PRICE = 1 // 价格区间
}

View File

@ -1,7 +1,7 @@
<template>
<footer class="layout-footer bg-[#f5f5f5] bg-opacity-90 text-center">
<div class="p-2 text-xs text-tx-secondary max-w-[900px] mx-auto">
<a href="http://beian.miit.gov.cn" class="my-[10px]">Copyright © 2023 粤ICP备2025364700号 - 广东粤好生活服务有限公司版权所有</a>
<a href="http://beian.miit.gov.cn" class="my-[10px]">Copyright © 2023 粤ICP备2025364700号 - 广东思缘生活服务有限公司版权所有</a>
</div>
</footer>
</template>

View File

@ -421,6 +421,20 @@ export const constantRoutes: Array<RouteRecordRaw> = [
}
}
]
},
{
path: '/setting/notice',
component: LAYOUT,
children: [
{
path: 'edit',
component: () => import('@/views/message/notice/edit.vue'),
meta: {
title: '设置',
activeMenu: '/setting/message/notice'
}
}
]
}
]

View File

@ -261,9 +261,9 @@ export function formatFileSize(bytes: number) {
*/
export function retain(value: any, n: any): string {
if (n === 'null' || n === 'undefined' || n === 0) return value
let tran = Math.round(value * Math.pow(10, n)) / Math.pow(10, n)
const tran = Math.round(value * Math.pow(10, n)) / Math.pow(10, n)
let tranV = tran.toString()
let newVal = tranV.indexOf('.')
const newVal = tranV.indexOf('.')
if (newVal < 0) {
tranV += '.'
}
@ -272,3 +272,40 @@ export function retain(value: any, n: any): string {
}
return tranV
}
export function formatString(input) {
const processNumber = numStr => {
if (!numStr.includes('.')) return numStr
// 分割整数和小数部分(处理 .00 和 20. 等情况)
const parts = numStr.split('.', 2)
let [intPart, decPart] = parts
// 处理类似 ".00" 的情况
intPart = intPart || '0'
// 处理小数部分全零或无小数部分的情况
if (!decPart || decPart.trimEnd().replace(/^0+$/, '') === '') {
return intPart
}
// 去除末尾零并处理格式问题
const strippedDec = decPart.trimEnd().replace(/^0+$/, '')
const result = `${intPart}.${strippedDec}`
// 处理特殊情况
if (strippedDec === '') return intPart // 123.000 → 123
if (result.endsWith('.')) return intPart // 123. → 123
if (result.startsWith('.') && strippedDec) return `0${result}` // .55 → 0.55
return result
}
if (!input) return
const parts = input.split('-', 2)
if (parts.length !== 2) return input
const left = processNumber(parts[0])
const right = processNumber(parts[1])
return `${left}-${right}`
}

View File

@ -1,4 +1,4 @@
import { DispatchCountEnum, DispatchEnum, UseConditionEnum, UseGoodsEnum } from '@/enums/modeEnum'
import { DispatchCountEnum, DispatchEnum, PriceEnum, UseConditionEnum, UseGoodsEnum } from '@/enums/modeEnum'
import type { UploadFile } from 'element-plus'
/*
@ -357,3 +357,31 @@ export function validateValue(rule: any, value: any, callback: any) {
}
}
}
export function validatePirce(args, payload: any) {
const [, value, callback] = args
const { priceType, minPrice, maxPrice } = payload
if (priceType == PriceEnum.CUSTOMER_PRICE) {
if (value == '') {
callback(new Error('请填写价格'))
} else if (!isNumberWithDot(value)) {
callback(new Error('价格必须大于或等于零'))
} else {
callback()
}
} else {
if (minPrice == '' || maxPrice == '') {
const errMsg = minPrice == '' ? '下限' : maxPrice == '' ? '上限' : ''
callback(new Error('请输入价格' + errMsg))
} else {
if (!isNumberWithDot(minPrice as string) || !isNumberWithDot(maxPrice as string)) {
callback(new Error('价格必须大于或等于零'))
} else if (Number(minPrice) > Number(maxPrice)) {
callback(new Error('价格下限不能高于上限'))
} else if (minPrice == maxPrice) {
callback(new Error('请输入有效区间,价格下限不能等于上限'))
} else {
callback()
}
}
}
}

View File

@ -6,12 +6,19 @@
<el-card shadow="never" style="margin-top: 15px" class="!border-none">
<!-- 用户绑定 -->
<el-form-item label="绑定用户:" prop="userId">
<user-select btn-text="" type="primary" v-model="formData.userId" :userVo="formData.userVo" :id="id"
v-model:userMobile="formData.mobile" :api="apiUserNotStaff" />
<user-select
btn-text="选择用户"
type="primary"
v-model="formData.userId"
:userVo="formData.userVo"
:id="id"
v-model:userMobile="formData.mobile"
:api="apiUserNotStaff"
/>
</el-form-item>
<el-form-item label="师傅编号:" prop="sn">
<!-- <el-form-item label="师傅编号:" prop="sn">
<el-input class="w-56 ls-input" :disabled="true" v-model="formData.sn" placeholder="请输入师傅编号" />
</el-form-item>
</el-form-item> -->
<el-form-item label="师傅名称:" prop="name">
<el-input class="w-56 ls-input" v-model="formData.name" placeholder="请输入师傅的真实姓名" />
</el-form-item>
@ -22,8 +29,7 @@
<!-- 服务项目 -->
<el-form-item label="服务项目" prop="goodsIds">
<el-select v-model="formData.goodsIds" placeholder="请选择" class="ls-input" clearable multiple>
<el-option v-for="category in categoryLists" :key="category.id" :label="category.name"
:value="category.id"></el-option>
<el-option v-for="category in categoryLists" :key="category.id" :label="category.name" :value="category.id"></el-option>
</el-select>
</el-form-item>
<!-- <div class="serviceItem">
@ -51,27 +57,37 @@
</el-form-item>
<!-- 所在地区 -->
<el-form-item label="所在地区:" prop="districtId">
<area-select class="w-56 ls-input" v-model:province="formData.provinceId" v-model:city="formData.cityId"
v-model:district="formData.districtId" />
<area-select
class="w-56 ls-input"
v-model:province="formData.provinceId"
v-model:city="formData.cityId"
v-model:district="formData.districtId"
/>
</el-form-item>
<el-form-item label="详细地址:">
<el-input class="ls-input" type="textarea" v-model="formData.address" placeholder="选填,请输入详细地址"
:rows="6"></el-input>
<el-input class="ls-input" type="textarea" v-model="formData.address" placeholder="选填,请输入详细地址" :rows="6"></el-input>
</el-form-item>
<el-form-item label="接单状态:" prop="isReceiveOrder">
<el-switch v-model="formData.isReceiveOrder" :active-text="!formData.isReceiveOrder ? '' : ''"
:active-value="0" :inactive-value="1" />
</el-form-item>
<el-form-item label="运营师傅:">
<el-switch v-model="formData.isOperational" :active-value="1" :inactive-value="0"
:active-text="formData.isOperational ? '是' : '否'" />
<!-- <el-form-item label="接单状态:" prop="isReceiveOrder">
<el-switch
v-model="formData.isReceiveOrder"
:active-text="!formData.isReceiveOrder ? '启用' : '停用'"
:active-value="0"
:inactive-value="1"
disabled
/>
</el-form-item> -->
<!-- <el-form-item label="运营师傅:">
<el-switch
v-model="formData.isOperational"
:active-value="1"
:inactive-value="0"
:active-text="formData.isOperational ? '是' : '否'"
/>
</el-form-item>
<el-form-item label="工作时间" prop="workStartTime" v-if="formData.isOperational === 1">
<el-time-picker v-model="formData.workStartTime" placeholder="开始时间" value-format="HH:mm" format="HH:mm"
class="h-[45px]" />
<el-time-picker v-model="formData.workEndTime" placeholder="结束时间" value-format="HH:mm" format="HH:mm"
class="h-[45px]" />
</el-form-item>
<el-time-picker v-model="formData.workStartTime" placeholder="开始时间" value-format="HH:mm" format="HH:mm" class="h-[45px]" />
<el-time-picker v-model="formData.workEndTime" placeholder="结束时间" value-format="HH:mm" format="HH:mm" class="h-[45px]" />
</el-form-item> -->
<!-- <el-form-item label="首页推荐:">
<el-radio v-model="formData.isRecommend" name="1" :label="1"></el-radio>
<el-radio v-model="formData.isRecommend" name="0" :label="0"></el-radio>
@ -84,18 +100,15 @@
</template>
<script lang="ts" setup>
import { apiMasterWorkerAdd, apiMasterWorkerEdit, apiMasterWorkerDetail, apiMasterWorkerAreaAdd, apiMasterWorkerArea } from '@/api/master_worker'
import { ref, reactive } from 'vue'
import { apiMasterWorkerAdd, apiMasterWorkerArea, apiMasterWorkerDetail, apiMasterWorkerEdit, apiUserNotStaff } from '@/api/master_worker'
import AreaSelect from '@/components/area-select/index.vue'
import FooterBtns from '@/components/footer-btns/index.vue'
import UserSelect from '@/components/user-select/index.vue'
import AreaSelect from '@/components/area-select/index.vue'
import ServiceSelect from '@/components/service-select/index.vue'
import ServiceArea from '@/components/service-area/index.vue'
import type { ElForm } from 'element-plus'
import feedback from '@/utils/feedback'
import { apiUserNotStaff } from '@/api/master_worker'
import { validateWorkTime } from '@/utils/validate'
import { useCommon } from '@/hooks/useCommon'
import feedback from '@/utils/feedback'
import { validateWorkTime } from '@/utils/validate'
import type { ElForm } from 'element-plus'
import { reactive, ref } from 'vue'
/** Interface Start **/
interface formDataObj {
@ -155,7 +168,7 @@ const formData = ref<formDataObj>({
isRecommend: 0, //
serviceArea: [],
serviceAreaIds: [],
isReceiveOrder: '',
isReceiveOrder: 0,
workStartTime: '',
workEndTime: '',
isOperational: 0
@ -181,7 +194,7 @@ const rules = reactive<object>({
//
const getMasterWorkerDetail = async (id: number): Promise<void> => {
; (formData.value as {}) = await apiMasterWorkerDetail({ id })
;(formData.value as {}) = await apiMasterWorkerDetail({ id })
if (!formData.value.goodsCategoryList.length) return
formData.value.goodsIds = formData.value.goodsCategoryList.map(category => category.id)
}
@ -221,6 +234,7 @@ const onSubmit = (formEl: FormInstance | undefined): void => {
// return true
// }
// =>
fetchCategoryList()
onMounted(async () => {
if (id) {
fetchCategoryList()

View File

@ -4,26 +4,26 @@
<el-form-item label="师傅信息">
<el-input class="ls-input" v-model="formData.staffInfo" placeholder="师傅编号/姓名/手机号" />
</el-form-item>
<el-form-item label="用户信息">
<!-- <el-form-item label="用户信息">
<el-input class="ls-input" v-model="formData.userInfo" placeholder="请输入用户昵称/用户编号/手机号" />
</el-form-item>
</el-form-item> -->
<el-form-item label="所在地区">
<el-cascader class="w-56 ls-input" :options="options" :props="props2" @change="handleSelect" ref="cascader" />
</el-form-item>
<el-form-item label="首页推荐">
<!-- <el-form-item label="首页推荐">
<el-select v-model="formData.isRecommend" placeholder="请选择" class="ls-input">
<el-option label="全部" value></el-option>
<el-option label="推荐" value="1"></el-option>
<el-option label="不推荐" value="0"></el-option>
</el-select>
</el-form-item>
<el-form-item label="师傅状态">
</el-form-item> -->
<!-- <el-form-item label="师傅状态">
<el-select v-model="formData.status" placeholder="请选择" class="ls-input">
<el-option label="全部" value></el-option>
<el-option label="启用" value="1"></el-option>
<el-option label="停用" value="0"></el-option>
</el-select>
</el-form-item>
</el-form-item> -->
<el-form-item>
<div class="flex">
<el-button type="primary" @click="getLists"></el-button>
@ -34,7 +34,7 @@
</el-card>
<el-card shadow="never" class="mt-4 !border-none">
<!-- <el-button type="primary" @click="$router.push('edit')"></el-button> -->
<el-button type="primary" @click="$router.push('edit')"></el-button>
<div class="mt-3">
<el-table :data="pager.lists" style="width: 100%" v-loading="pager.loading">
<el-table-column property="sn" label="师傅编号" min-width="110">
@ -45,8 +45,8 @@
<div class="flex items-center">
<el-image
class="avatar"
:src="row.headPortrait"
:preview-src-list="[row.headPortrait]"
:src="row.avatarUrl"
:preview-src-list="[row.avatarUrl]"
:hide-on-click-modal="true"
:preview-teleported="true"
fit="cover"
@ -63,7 +63,7 @@
<div>{{ scope.row.province }}{{ scope.row.city }}{{ scope.row.district }}</div>
</template>
</el-table-column>
<el-table-column prop="idCard" label="身份证号码" min-width="200"></el-table-column>
<!-- <el-table-column prop="idCard" label="身份证号码" min-width="200"></el-table-column>
<el-table-column prop="idCardImg" label="身份证正反面" min-width="200" align="center">
<template #default="{ row }">
<el-image
@ -75,7 +75,6 @@
/>
</template>
</el-table-column>
<el-table-column prop="physicalExamination" min-width="200" label="体检报告图片和pdf">
<template #default="{ row }">
<div class="method" v-if="row.physicalExamination">
@ -104,24 +103,32 @@
<template #default="scope">
<el-popover placement="top-start" title="提示:" trigger="hover" content="点击可跳转服务评价页面">
<template #reference>
<router-link :to="{ path: '/service/evaluate', query: { staffInfo: scope.row.sn } }">
<div class="w-[50px] text-center text-[#4A5DFF]">{{ scope.row.score }}</div>
<router-link
:to="{
path: '/service/evaluate',
query: { staffInfo: scope.row.sn }
}"
>
<div class="w-[50px] text-center text-[#4A5DFF]">
{{ scope.row.score }}
</div>
</router-link>
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column label="接单状态" min-width="80">
</el-table-column> -->
<!-- <el-table-column label="接单状态" min-width="80">
<template #default="scope">
<el-switch
v-model="scope.row.isReceiveOrder"
:active-value="0"
:inactive-value="1"
:before-change="() => handleStatusChange(scope.row)"
disabled
/>
</template>
</el-table-column>
<el-table-column label="运营师傅" min-width="80">
</el-table-column> -->
<!-- <el-table-column label="运营师傅" min-width="80">
<template #default="scope">
<el-switch
v-model="scope.row.isOperational"
@ -130,18 +137,18 @@
:before-change="() => handleIsOperationalChange(scope.row)"
/>
</template>
</el-table-column>
</el-table-column> -->
<!-- <el-table-column property="recommend_desc" label="首页推荐" min-width="160">
<template #default="scope">
<el-tag type="info" v-if="scope.row.isRecommend == 0"></el-tag>
<el-tag v-if="scope.row.isRecommend == 1"></el-tag>
</template>
</el-table-column> -->
<el-table-column prop="getType" label="金额详情" min-width="110">
<!-- <el-table-column prop="getType" label="金额详情" min-width="110">
<template #default="{ row }">
<MoneyDetailDialog btn-text="" :rowData="row" />
</template>
</el-table-column>
</el-table-column> -->
<el-table-column property="createTime" label="添加时间" min-width="180" />
<el-table-column label="操作" width="148" fixed="right">
<template #default="scope">
@ -170,17 +177,13 @@
</template>
<script lang="ts" setup name="masterList">
import { apiMasterWorkerLists, apiMasterWorkerDel, apiMasterWorkerStatusEdit, apiStaffIsOperationalUpdate } from '@/api/master_worker'
import { ref } from 'vue'
import { apiMasterWorkerDel, apiMasterWorkerLists, apiMasterWorkerStatusEdit, apiStaffIsOperationalUpdate } from '@/api/master_worker'
import Pagination from '@/components/pagination/index.vue'
import MoneyDetailDialog from '@/components/money-detail/moneyDetailDialog.vue'
import { ReceiveOrderEnum } from '@/enums/modeEnum'
import { usePaging } from '@/hooks/usePaging'
import area from '@/utils/area'
import feedback from '@/utils/feedback'
import { ReceiveOrderEnum } from '@/enums/modeEnum'
import PdfImg from '@/assets/icons/pdf.svg'
import reportImg from '@/assets/images/report.png'
import IdCardImg from '@/assets/images/idCard.png'
import { ref } from 'vue'
const router = useRouter()
@ -216,7 +219,7 @@ const isVisiblePdf = computed(() => {
})
const previewIdCardList = computed(() => {
return (row: any) => {
return row.idCardImg.split(',')
return row.idCardImg?.split(',')
}
})
const { pager, getLists, resetPage, resetParams } = usePaging({
@ -268,7 +271,10 @@ const handleIsOperationalChange = (row: any) => {
const handlePreviewPdf = (row: any) => {
const { physicalExamination } = row
if (!physicalExamination) return
const url = router.resolve({ path: '/report/pdf', query: { path: physicalExamination?.split(',').filter(item => item.includes('pdf')) } })
const url = router.resolve({
path: '/report/pdf',
query: { path: physicalExamination?.split(',').filter(item => item.includes('pdf')) }
})
window.open(url.href)
}
getLists()

View File

@ -3,18 +3,12 @@
<el-card class="!border-none" shadow="never">
<el-page-header :content="$route.meta.title" @back="$router.back()" />
</el-card>
<el-form
ref="formRef"
:model="formData"
label-width="120px"
:rules="rules"
v-loading="loading"
>
<el-form ref="formRef" :model="formData" label-width="120px" :rules="rules" v-loading="loading">
<el-card class="!border-none mt-4" shadow="never">
<div class="font-medium mb-7">通知名称</div>
<el-form-item label="通知名称" prop="name"> {{ formData.name }} </el-form-item>
<el-form-item label="通知类型" prop="name"> {{ formData.type }} </el-form-item>
<el-form-item label="通知业务" prop="name"> {{ formData.remarks }} </el-form-item>
<el-form-item label="通知名称" prop="name">{{ formData.name }}</el-form-item>
<el-form-item label="通知类型" prop="name">{{ formData.type }}</el-form-item>
<el-form-item label="通知业务" prop="name">{{ formData.remarks }}</el-form-item>
</el-card>
<el-card class="!border-none mt-4" shadow="never">
<div class="font-medium mb-7">短信通知</div>
@ -26,20 +20,13 @@
</el-form-item>
<el-form-item label="模板ID" prop="smsNotice.templateId">
<div class="w-80">
<el-input
v-model="formData.smsNotice.templateId"
placeholder="请输入模板ID"
/>
<el-input v-model="formData.smsNotice.templateId" placeholder="请输入模板ID" />
</div>
</el-form-item>
<el-form-item label="短信内容" prop="smsNotice.content">
<div class="flex-1">
<div class="w-full max-w-[320px]">
<el-input
type="textarea"
:autosize="{ minRows: 6, maxRows: 6 }"
v-model="formData.smsNotice.content"
/>
<el-input type="textarea" :autosize="{ minRows: 6, maxRows: 6 }" v-model="formData.smsNotice.content" />
</div>
<div class="form-tips">
<!-- <div v-for="(item, index) in formData.smsNotice.tips" :key="index"> -->
@ -103,7 +90,7 @@ const getDetails = async () => {
const data = await noticeDetail({
id: route.query.id
})
Object.keys(data).forEach((key) => {
Object.keys(data).forEach(key => {
//@ts-ignore
formData[key] = data[key]
})
@ -114,7 +101,7 @@ const handleSave = async () => {
await formRef.value?.validate()
await setNoticeConfig(formData)
feedback.msgSuccess('操作成功')
removeTab()
// removeTab()
router.back()
}

View File

@ -1,22 +1,11 @@
<template>
<div>
<el-card class="!border-none" shadow="never">
<el-alert
type="warning"
title="温馨提示:平台配置在各个场景下的通知发送方式和内容模板"
:closable="false"
show-icon
></el-alert>
<el-alert type="warning" title="温馨提示:平台配置在各个场景下的通知发送方式和内容模板" :closable="false" show-icon></el-alert>
</el-card>
<el-card class="!border-none mt-4" shadow="never">
<el-tabs v-model="tabsActive" @tab-change="getLists">
<el-tab-pane
v-for="(item, index) in tabsMap"
:key="index"
:label="item.name"
:name="item.type"
lazy
></el-tab-pane>
<el-tab-pane v-for="(item, index) in tabsMap" :key="index" :label="item.name" :name="item.type" lazy></el-tab-pane>
</el-tabs>
<el-table size="large" :data="state.lists" v-loading="state.loading">
<el-table-column label="通知场景" prop="name" min-width="120" />
@ -29,18 +18,7 @@
</el-table-column>
<el-table-column label="操作" min-width="80" fixed="right">
<template #default="{ row }">
<el-button v-perms="['setting:notice:detail']" type="primary" link>
<router-link
:to="{
path: getRoutePath('setting:notice:detail'),
query: {
id: row.id
}
}"
>
设置
</router-link>
</el-button>
<el-button v-perms="['setting:notice:detail']" type="primary" link @click="handleSetting(row.id)"></el-button>
</template>
</el-table-column>
</el-table>
@ -49,7 +27,6 @@
</template>
<script lang="ts" setup name="notice">
import { noticeLists } from '@/api/message'
import { getRoutePath } from '@/router'
enum NoticeEnums {
USER = 1,
@ -91,7 +68,10 @@ const getLists = async () => {
state.loading = false
}
}
const router = useRouter()
const handleSetting = (id: number) => {
router.push({ path: '/setting/notice/edit', query: { id } })
}
onActivated(() => {
getLists()
})

View File

@ -14,32 +14,32 @@
</div>
</template>
<!-- 订单信息 -->
<el-form :inline="true" :model="formData" label-width="auto">
<el-form :inline="true" :model="formData" label-width="auto" class="order-form">
<el-form-item label="订单状态: ">
<div class="content text-warning">{{ formData.orderStatusName || '-' }}</div>
</el-form-item>
<el-form-item label="支付状态: ">
<!-- <el-form-item label="支付状态: ">
<div class="content">{{ formData.payStatusName || '-' }}</div>
</el-form-item>
</el-form-item> -->
<el-form-item label="订单编号: ">
<div class="content">{{ formData.sn || '-' }}</div>
</el-form-item>
<el-form-item label="支付方式: ">
<!-- <el-form-item label="支付方式: ">
<div class="content">{{ formData.payWayName || '-' }}</div>
</el-form-item>
</el-form-item> -->
<el-form-item label="下单时间: ">
<div class="content">{{ formData.createTime || '-' }}</div>
</el-form-item>
<el-form-item label="支付时间: ">
<!-- <el-form-item label="支付时间: ">
<div class="content">{{ formData.payTime || '-' }}</div>
</el-form-item>
</el-form-item> -->
<el-form-item label="用户昵称: ">
<div class="content">{{ formData.nickname || '-' }}</div>
</el-form-item>
<el-form-item label="用户备注: ">
<div class="content">{{ formData.userRemark || '-' }}</div>
</el-form-item>
<!-- <el-form-item label="核销码: ">
<el-form-item label="核销码: ">
<div class="content">{{ formData.verificationCode || '-' }}</div>
</el-form-item>
<el-form-item label="核销状态: ">
@ -49,13 +49,18 @@
</span>
<span v-else>-</span>
</div>
</el-form-item> -->
</el-form-item>
<el-form-item label="完成时间: ">
<div class="content break-words">{{ formData.finishTime || '-' }}</div>
</el-form-item>
<el-form-item label="商家备注: ">
<div class="w-[300px] break-words">{{ formData.orderRemarks! || '-' }}</div>
</el-form-item>
<el-form-item label="退款原因: " v-if="formData.orderStatus == 4">
<div class="content">
{{ formData?.orderRefundDetailVo?.refundReason || '-' }}
</div>
</el-form-item>
</el-form>
<!-- Button Group Start -->
<div class="button-group">
@ -112,7 +117,7 @@
</el-card>
<!-- 服务师傅 Start -->
<!-- <el-card class="!border-none mt-4" shadow="never" style="padding: 0 20px" v-if="formData.orderStatus !== 0">
<el-card class="!border-none mt-4" shadow="never" style="padding: 0 20px" v-if="formData.orderStatus !== 0">
<template #header>
<span class="font-medium nr">服务师傅</span>
</template>
@ -160,11 +165,11 @@
</div>
</el-form-item>
</el-form>
</el-card> -->
</el-card>
<!-- 服务师傅 End -->
<!-- 历史服务师傅 -->
<!-- <el-card
<el-card
class="!border-none mt-4"
shadow="never"
style="padding: 0 20px"
@ -179,14 +184,18 @@
<div class="ml-2">
{{ `${staff.staffName}${staff.staffSn}` }}
</div>
<div>
<!-- <div>
上门服务时间{{ staff.timeBefore < 0 ? '后' : '前' }}{{ staff.timeBefore.toString().replace(/\-/g, ' ') }}分钟退单
<span style="color: #f56c6c">{{ staff.deductScore }}</span>
(改派时间{{ staff.createTime }})
</div> -->
<div>
上门服务时间{{ staff.timeBefore < 0 ? '后' : '前'
}}{{ staff.timeBefore.toString().replace(/\-/g, ' ') }}分钟退单改派时间{{ staff.createTime }}
</div>
</div>
</div>
</el-card> -->
</el-card>
<!-- 预约信息 Start -->
<el-card class="!border-none mt-4" shadow="never" style="padding: 0 20px">
@ -230,7 +239,9 @@
</div>
</template>
</el-table-column>
<el-table-column property="goodsPrice" label="价格" width="120" />
<el-table-column property="goodsPrice" label="价格" width="120">
<template #default="scope">¥{{ parsePrice(scope.row) }}</template>
</el-table-column>
<el-table-column property="unitName" label="单位" width="120" />
<el-table-column property="goodsNum" label="数量" width="120" />
<el-table-column property="totalAmount" label="总价格" width="120" />
@ -238,7 +249,7 @@
<template #default="{ row }">{{ isUseCoupon(row) }}</template>
</el-table-column> -->
<!-- <el-table-column property="deductionMoney" label="抵扣金额" width="120" fixed="right" /> -->
<el-table-column property="orderAmount" label="实付金额" width="120" fixed="right" />
<!-- <el-table-column property="orderAmount" label="实付金额" width="120" fixed="right" /> -->
</el-table>
</el-card>
<!-- 服务信息 End -->
@ -283,17 +294,16 @@
</template>
<script lang="ts" setup>
import { apiOrderDetail, apiMasterLists, apiDispatchStaff, apiDispatchStaffScore } from '@/api/order/lists'
import { ref } from 'vue'
import Operation from './components/operation.vue'
import dispatchDialog from './components/dispatchDialog.vue'
import { apiDispatchStaff, apiDispatchStaffScore, apiMasterLists, apiOrderDetail } from '@/api/order/lists'
import { PriceEnum, ReceiveOrderEnum, getTypeMap, useGoodsTypeMap } from '@/enums/modeEnum'
import { useCreateModal } from '@/hooks/useCreateModal'
import area from '@/utils/area'
import feedback from '@/utils/feedback'
import { useGoodsTypeMap, getTypeMap, ReceiveOrderEnum } from '@/enums/modeEnum'
import { parseEmpty, timeFormat } from '@/utils/util'
import { parseCouponTime } from '@/utils/util'
import { useCreateModal } from '@/hooks/useCreateModal'
import { formatString, parseCouponTime, parseEmpty } from '@/utils/util'
import { ref } from 'vue'
import cancelOrderDialog from './components/cancelOrderDialog.vue'
import dispatchDialog from './components/dispatchDialog.vue'
import Operation from './components/operation.vue'
import { useCancelOrderAction } from './hook'
const route = useRoute()
@ -321,7 +331,9 @@ const goodsData = ref<any>([
deductionMoney: '',
totalAmount: '',
orderAmount: '',
couponDetailVo: {}
couponDetailVo: {},
priceType: PriceEnum.CUSTOMER_PRICE,
priceRange: ''
}
])
const couponInfo = ref([
@ -387,6 +399,10 @@ const orderRefundDetailVo = computed(() => (unref(isShowOrderRefundDetailVo) ? u
/**是否派发给师傅 */
const isDispatch = computed(() => (status: number) => status === ReceiveOrderEnum.PAUSE)
const parsePrice = computed(() => row => {
const { priceType, priceRange, goodsPrice } = row
return priceType == PriceEnum.CUSTOMER_PRICE ? goodsPrice : formatString(priceRange)
})
function generateCouponFields(res: any) {
const fields = {
@ -423,7 +439,8 @@ const handleChange = (val: any) => {
return val
}
const areaValue = ref([440000, 440100, 440118])
// const areaValue = ref([440000, 440100, 440118])
const areaValue = ref([])
//
const handleSelect = async (val: number[]) => {
//
@ -435,13 +452,24 @@ const handleSelect = async (val: number[]) => {
isReceiveOrder: ReceiveOrderEnum.ENABLEDING,
isOrder: ReceiveOrderEnum.ENABLEDING,
appointTimeStart: unref(formData).appointTimeStart,
appointTimeEnd: unref(formData).appointTimeEnd
appointTimeEnd: unref(formData).appointTimeEnd,
goodsId: goodsData.value[0].goodsId,
staffId: formData.value.staffId
}
const { lists } = await apiMasterLists({ ...params })
staffData.value = lists
areaValue.value = val
}
watch(
() => formData.value.staffId,
newVal => {
if (newVal) {
const { provinceId, cityId, districtId } = formData.value
areaValue.value = [provinceId, cityId, districtId]
handleSelect(areaValue.value)
}
}
)
//
const handleDispatch = async () => {
getOrderDetail()
@ -517,7 +545,7 @@ const handleReDispatch = () => {
//
const handleCancel = () => {
btnStatus.value = 2
btnStatus.value = formData.value.staffId ? 2 : 0
selStaff.value = ''
getOrderDetail()
}
@ -542,4 +570,8 @@ onMounted(async () => {
.button-group {
border-top: 1px solid #f2f2f2;
}
.order-form {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
</style>

View File

@ -10,19 +10,19 @@
<el-form-item label="服务名称">
<el-input class="ls-input" v-model="formData.goodsName" placeholder="请输入服务名称" />
</el-form-item>
<!-- <el-form-item label="师傅信息">
<el-form-item label="师傅信息">
<el-input class="ls-input" v-model="formData.staffInfo" placeholder="请输入师傅信息" clearable />
</el-form-item> -->
</el-form-item>
<el-form-item label="支付状态">
<el-select v-model="formData.payStatus" placeholder="请选择" class="ls-input">
<el-option v-for="option in optionMap.payStatusOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
<!-- <el-form-item label="派单状态">
<el-form-item label="派单状态">
<el-select v-model="formData.isDispatch" placeholder="请选择" class="ls-input">
<el-option v-for="option in optionMap.dispatchOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</el-form-item> -->
</el-form-item>
<el-form-item label="下单时间">
<data-picker
class="ls-input"
@ -42,9 +42,8 @@
<el-tabs class="-mt-2" v-model="formData.orderStatus" @tab-change="resetPage">
<el-tab-pane :label="`全部(${countData?.totalCount || '0'})`" name="" />
<el-tab-pane :label="`待支付(${countData?.waitPayCount || '0'})`" :name="0" />
<!-- <el-tab-pane :label="`预约中(${countData?.reserveCount || '0'})`" :name="1" />
<el-tab-pane :label="`服务中(${countData?.servicingCount || '0'})`" :name="2" /> -->
<el-tab-pane :label="`已预约(${countData?.servicingCount || '0'})`" :name="2" />
<el-tab-pane :label="`预约中(${countData?.reserveCount || '0'})`" :name="1" />
<el-tab-pane :label="`服务中(${countData?.servicingCount || '0'})`" :name="2" />
<el-tab-pane :label="`已完成(${countData?.finishedCount || '0'})`" :name="3" />
<el-tab-pane :label="`退款单(${countData?.closeCount || '0'})`" :name="4" />
</el-tabs>
@ -91,23 +90,26 @@
</div>
<router-link :to="`/service/lists/edit?id=${row.goodsId}`" class="ml-2 xs">
<div class="goods-name truncate w-44">{{ row.goodsName }}</div>
<div class="form-tips">{{ row.goodsPrice }} {{ row.unitName }}</div>
<div class="form-tips">{{ parsePrice(row) }} {{ row.unitName }}</div>
</router-link>
</div>
</template>
</el-table-column>
<el-table-column label="实付金额" min-width="100">
<template #default="scope">¥{{ scope.row.orderAmount }}</template>
<el-table-column label="服务金额" min-width="100">
<template #default="scope">¥{{ parsePrice(scope.row) }}</template>
</el-table-column>
<el-table-column label="预约日期" min-width="160">
<!-- <el-table-column label="实付金额" min-width="100">
<template #default="scope">¥{{ scope.row.orderAmount }}</template>
</el-table-column> -->
<el-table-column label="预约日期" min-width="180">
<template #default="scope">
{{ scope.row.appointTime }} {{ scope.row.weekDay }} {{ scope.row.appointTimeStartStr }}-{{ scope.row.appointTimeEndStr }}
</template>
</el-table-column>
<el-table-column property="contact" label="联系人" min-width="100" />
<!-- <el-table-column property="staffName" label="服务师傅" min-width="100">
<el-table-column property="staffName" label="服务师傅" min-width="100">
<template #default="{ row }">{{ parseEmpty(row, 'staffName') }}</template>
</el-table-column> -->
</el-table-column>
<el-table-column label="订单状态" min-width="100">
<template v-slot="{ row }">
<span
@ -119,12 +121,12 @@
</span>
</template>
</el-table-column>
<!-- <el-table-column label="派单状态" width="100" prop="isDispatchName">
<el-table-column label="派单状态" width="100" prop="isDispatchName">
<template v-slot="{ row }">
<el-tag type="success" v-if="row.isDispatch === 1"></el-tag>
<el-tag type="danger" v-if="row.isDispatch === 0"></el-tag>
</template>
</el-table-column> -->
</el-table-column>
<el-table-column label="操作" min-width="140" fixed="right">
<template #default="scope">
<router-link
@ -163,13 +165,13 @@
import { apiOrderLists, apiOrderStatis } from '@/api/order/lists'
import { reactive, onMounted, computed } from 'vue'
import { useRoute } from 'vue-router'
import { OrderMode } from '@/enums/modeEnum'
import { OrderMode, PriceEnum } from '@/enums/modeEnum'
import Operation from './components/operation.vue'
import Pagination from '@/components/pagination/index.vue'
import DataPicker from '@/components/daterange-picker/index.vue'
import cancelOrderDialog from './components/cancelOrderDialog.vue'
import { usePaging } from '@/hooks/usePaging'
import { parseEmpty } from '@/utils/util'
import { formatString, parseEmpty } from '@/utils/util'
import { optionMap } from '@/config/status'
import { useCancelOrderAction } from './hook'
@ -183,6 +185,8 @@ interface formDataObj {
orderStatus: string | number // int ;0-;1-;2-;3-;4-
staffInfo: string //
isDispatch: string | number // int ;0-;1-
priceType: PriceEnum
priceRange: string
}
const route = useRoute()
@ -196,7 +200,9 @@ const formData = reactive<formDataObj>({
orderTimeEnd: '',
orderStatus: '',
staffInfo: route.query.staffInfo ?? '',
isDispatch: ''
isDispatch: '',
priceType: PriceEnum.CUSTOMER_PRICE,
priceRange: ''
})
const getOrderAmount = async () => {
countData.value = await apiOrderStatis(formData)
@ -206,6 +212,10 @@ const setStatusColor = computed(() => {
return val === '待支付' || val === '已退款' || val === '退款中'
}
})
const parsePrice = computed(() => row => {
const { priceType, priceRange, goodsPrice } = row
return priceType == PriceEnum.CUSTOMER_PRICE ? goodsPrice : formatString(priceRange)
})
const { pager, getLists, resetPage, resetParams } = usePaging({
fetchFun: apiOrderLists,

View File

@ -26,13 +26,24 @@
<div class="form-tips">建议尺寸750*750可拖拽改变图片顺序最多上传10张</div>
</div>
</el-form-item>
<el-form-item label="前台价格:" prop="price">
<el-input class="w-56 ls-input" v-model="modelValue.price" placeholder="请输入价格" />
<el-form-item label="前台价格:" prop="price" class="price">
<el-radio-group v-model="modelValue.priceType">
<el-radio v-for="item in priceOptions" :key="item.value" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
<template v-if="modelValue.priceType == PriceEnum.CUSTOMER_PRICE">
<el-input class="w-56 ls-input" v-model="modelValue.price" placeholder="请输入价格" />
</template>
<template v-else-if="modelValue.priceType == PriceEnum.TEACHER_PRICE">
<div>
<el-input class="w-56 ls-input" v-model="modelValue.minPrice" placeholder="请输入价格" />
<span class="mx-[6px]"></span>
<el-input class="w-56 ls-input" v-model="modelValue.maxPrice" placeholder="请输入价格" />
</div>
</template>
</el-form-item>
<el-form-item label="折前价格:" prop="scribingPrice">
<!-- <el-form-item label="折前价格:" prop="scribingPrice">
<el-input class="w-56 ls-input" v-model="modelValue.scribingPrice" placeholder="请输入折前价格" />
</el-form-item>
</el-form-item> -->
<el-form-item label="单位:" prop="unitId">
<!-- 选择单位 -->
<el-select v-model="modelValue.unitId" class="w-56 select" placeholder="请选择">
@ -80,6 +91,7 @@ import { apiGetGoodsTimeAllList } from '@/api/service/subscribe'
import { reactive, ref } from 'vue'
import MaterialPicker from '@/components/material/picker.vue'
import unitForm from '../../unit/components/unit-form.vue'
import { PriceEnum } from '@/enums/modeEnum'
interface Lists {
name: string
@ -95,6 +107,10 @@ withDefaults(
}
)
const priceOptions = shallowRef([
{ label: '固定价格', value: PriceEnum.CUSTOMER_PRICE },
{ label: '价格区间', value: PriceEnum.TEACHER_PRICE }
])
const categoryData = ref<Array<Lists> | null>([])
const unitData = ref<Array<Lists> | null>([])
const goodsTimeData = ref<Array<Lists> | null>([])
@ -133,6 +149,11 @@ const toAddCategory = () => {
getCategoryLists()
getUnitCommonLists()
getGoodsTimeLists()
const formItemRef = ref()
defineExpose({
formItemRef
})
</script>
<style lang="scss" scoped>
@ -144,4 +165,12 @@ getGoodsTimeLists()
width: 340px;
margin-right: 10px;
}
.price {
:deep(> .el-form-item__content) {
display: flex;
flex-direction: column;
align-items: normal;
gap: 6px;
}
}
</style>

View File

@ -38,7 +38,8 @@ import editorVue from '@/components/editor/index.vue'
import FooterBtns from '@/components/footer-btns/index.vue'
import type { ElForm, FormRules } from 'element-plus'
import feedback from '@/utils/feedback'
import { isNumber, isNumberWithDot, validateServiceCommissionRate } from '@/utils/validate'
import { isNumber, isNumberWithDot, validatePirce, validateServiceCommissionRate } from '@/utils/validate'
import { PriceEnum } from '@/enums/modeEnum'
interface formDataObj {
name?: string
@ -56,6 +57,10 @@ interface formDataObj {
distributorCommissionRate: number
staffCommissionRate: number
goodsTimeId?: string
priceType: number
priceRange: Array<{ minPrice: number | string; maxPrice: number | string }>
maxPrice: number | string
minPrice: number | string
}
type FormInstance = InstanceType<typeof ElForm>
const formRef = ref<FormInstance>()
@ -72,7 +77,11 @@ const formData = ref<formDataObj>({
remarks: '',
categoryId: '',
image: '',
price: '',
priceType: PriceEnum.CUSTOMER_PRICE,
price: '', //
priceRange: [], //
minPrice: '', //-
maxPrice: '', //-
scribingPrice: '',
unitId: '',
status: 0,
@ -90,7 +99,7 @@ const rules = ref<FormRules>({
name: [{ required: true, message: '请输入服务名称', trigger: 'blur' }],
categoryId: [{ required: true, message: '请选择分类', trigger: 'change' }],
// image: [{ required: true, message: '', trigger: 'change' }],
price: [{ required: true, message: '请输入价格', trigger: 'blur' }],
price: [{ validator: (...args) => validatePirce(args, formData.value), trigger: 'blur' }],
unitId: [{ required: true, message: '请选择单位', trigger: 'change' }],
status: [{ required: true, message: '请选择商品状态', trigger: 'change' }],
isNewRecommend: [{ required: true, message: '请选择新品推荐', trigger: 'change' }],
@ -120,12 +129,13 @@ const getServiceDetail = async (): Promise<void> => {
//
const handleServiceAdd = async (): Promise<void> => {
// const str = formData.value.image.join(',')
if (!isNumberWithDot(formData.value.price as string)) return feedback.msgError('价格必须大于或等于零')
if (formData.value.scribingPrice && !isNumberWithDot(formData.value.scribingPrice as string)) return feedback.msgError('折前价格必须为纯数字')
if (formData.value.scribingPrice && Number(formData.value.price) > Number(formData.value.scribingPrice))
return feedback.msgError('前台价格高于折前价格,不合理定价!')
// if (!isNumberWithDot(formData.value.price as string)) return feedback.msgError('')
// if (formData.value.scribingPrice && !isNumberWithDot(formData.value.scribingPrice as string)) return feedback.msgError('')
// if (formData.value.scribingPrice && Number(formData.value.price) > Number(formData.value.scribingPrice))
// return feedback.msgError('')
// 0
// !formData.value.scribingPrice && (formData.value.scribingPrice = '0')
console.log(formData.value)
await apiServiceAdd({ ...formData.value })
router.back()
feedback.msgSuccess('操作成功')
@ -135,11 +145,10 @@ const handleServiceAdd = async (): Promise<void> => {
const handleServiceEdit = async (): Promise<void> => {
// const str = formData.value.image.join(',')
if (!isNumber(formData.value.sort as string)) return feedback.msgError('排序必须为纯数字')
if (!isNumberWithDot(formData.value.price as string)) return feedback.msgError('价格必须大于或等于零')
if (formData.value.scribingPrice && !isNumberWithDot(formData.value.scribingPrice as string)) return feedback.msgError('折前价格必须为纯数字')
if (formData.value.scribingPrice && Number(formData.value.price) > Number(formData.value.scribingPrice))
return feedback.msgError('前台价格高于折前价格,不合理定价!')
console.log('formData.value', formData.value)
// if (!isNumberWithDot(formData.value.price as string)) return feedback.msgError('')
// if (formData.value.scribingPrice && !isNumberWithDot(formData.value.scribingPrice as string)) return feedback.msgError('')
// if (formData.value.scribingPrice && Number(formData.value.price) > Number(formData.value.scribingPrice))
// return feedback.msgError('')
// 0
// !formData.value.scribingPrice && (formData.value.scribingPrice = '0')
@ -162,6 +171,21 @@ const onSubmit = (formEl: FormInstance | undefined): void => {
}
if (id) getServiceDetail()
watch(
() => formData.value.priceType,
(newVal, oldVal) => {
if (newVal !== oldVal) {
if (newVal == PriceEnum.TEACHER_PRICE) {
formData.value.price = ''
formRef.value?.clearValidate(['price'])
} else {
formData.value.minPrice = ''
formData.value.maxPrice = ''
}
}
}
)
</script>
<style lang="scss">

View File

@ -74,16 +74,17 @@
<el-table-column property="category" label="服务分类" min-width="145" />
<el-table-column property="price" label="前台价格" min-width="80">
<template #default="scope">
<el-input v-if="isEdit" v-model="scope.row.price" placeholder="请输入价格"></el-input>
<div v-if="!isEdit">¥{{ scope.row.price }}</div>
{{ parsePrice(scope.row) }}
<!-- <el-input v-if="isEdit" v-model="scope.row.price" placeholder="请输入价格"></el-input>
<div v-if="!isEdit">¥{{ scope.row.price }}</div> -->
</template>
</el-table-column>
<el-table-column property="scribingPrice" label="折前价格" min-width="80">
<!-- <el-table-column property="scribingPrice" label="折前价格" min-width="80">
<template #default="scope">
<el-input v-if="isEdit" v-model="scope.row.scribingPrice" placeholder="请输入价格"></el-input>
<div v-if="!isEdit">{{ scope.row.scribingPrice ? '' + scope.row.scribingPrice : '' }}</div>
</template>
</el-table-column>
</el-table-column> -->
<el-table-column property="unit" label="单位" min-width="80" />
<el-table-column property="orderNum" label="预约人数" min-width="80" />
<el-table-column property="statusDesc" label="销售状态" min-width="80">
@ -159,6 +160,7 @@ import { ref } from 'vue'
import Pagination from '@/components/pagination/index.vue'
import { usePaging } from '@/hooks/usePaging'
import feedback from '@/utils/feedback'
import { PriceEnum } from '@/enums/modeEnum'
interface formDataObj {
name: string //
@ -174,6 +176,10 @@ const formData = ref<formDataObj>({
status: '',
categoryId: ''
})
const parsePrice = computed(() => row => {
const { priceType, price, minPrice, maxPrice } = row
return priceType == PriceEnum.CUSTOMER_PRICE ? price + '元' : minPrice + '-' + maxPrice + '元'
})
const getOrderAmount = async () => {
countData.value = await apiServiceStatis({ ...formData.value, status: '' })

View File

@ -0,0 +1,96 @@
<template>
<div class="edit-popup">
<popup ref="popupRef" :title="popupTitle" :async="true" width="550px" @confirm="handleSubmit" @close="handleClose">
<el-form ref="formRef" :model="formData" label-width="84px" :rules="formRules">
<el-form-item label="客服姓名" prop="name">
<el-input v-model="formData.name" placeholder="请输入客服姓名" />
</el-form-item>
<el-form-item label="客服电话" prop="phone">
<el-input v-model="formData.phone" placeholder="请输入客服电话" />
</el-form-item>
<el-form-item label="短信通知" prop="enable" required>
<el-radio-group v-model="formData.enable">
<el-radio :label="1">开启</el-radio>
<el-radio :label="0">关闭</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</popup>
</div>
</template>
<script lang="ts" setup>
import { addOperation, editOperation, operationDetail } from '@/api/setting/operation_manager'
import Popup from '@/components/popup/index.vue'
import feedback from '@/utils/feedback'
import { validateContact } from '@/utils/validate'
import type { FormInstance } from 'element-plus'
const emit = defineEmits(['success', 'close'])
const formRef = shallowRef<FormInstance>()
const popupRef = shallowRef<InstanceType<typeof Popup>>()
const mode = ref('add')
const popupTitle = computed(() => {
return mode.value == 'edit' ? '编辑客服' : '新增客服'
})
const formData = reactive({
enable: 1,
name: '',
phone: ''
})
const formRules = reactive({
name: [
{
required: true,
message: '请输入客服姓名',
trigger: ['blur', 'change']
}
],
phone: [
{
required: true,
message: '请输入客服电话',
trigger: ['blur', 'change']
},
{
validator: validateContact,
trigger: 'change'
}
]
})
const handleSubmit = async () => {
await formRef.value?.validate()
mode.value == 'edit' ? await editOperation(formData) : await addOperation(formData)
popupRef.value?.close()
feedback.msgSuccess('操作成功')
emit('success')
}
const open = (type = 'add') => {
mode.value = type
popupRef.value?.open()
}
const setFormData = async (row: any) => {
const data = await operationDetail({
id: row.id
})
for (const key in formData) {
if (data[key] != null && data[key] != undefined) {
//@ts-ignore
formData[key] = data[key]
formData.id = data.id
}
}
}
const handleClose = () => {
emit('close')
}
defineExpose({
open,
setFormData
})
</script>

View File

@ -0,0 +1,97 @@
<template>
<div class="admin">
<el-card v-loading="pager.loading" class="mt-4 !border-none" shadow="never">
<el-button v-perms="['setting:operation_manager:add']" type="primary" @click="handleAdd">
<template #icon>
<icon name="el-icon-Plus" />
</template>
新增客服
</el-button>
<div class="mt-4">
<el-table :data="pager.lists" size="large">
<el-table-column label="客服姓名" prop="name" min-width="100" />
<el-table-column label="客服电话" prop="phone" min-width="100" />
<el-table-column label="短信通知" min-width="80">
<template #default="{ row }">
<el-switch :model-value="row.enable" :active-value="1" :inactive-value="0" @change="changeStatus(row)" />
</template>
</el-table-column>
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button v-perms="['setting:operation_manager:edit']" type="primary" link @click="handleEdit(row)"></el-button>
<el-button v-perms="['setting:operation_manager:delete']" type="danger" link @click="handleDelete(row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="flex mt-4 justify-end">
<pagination v-model="pager" @change="getLists" />
</div>
</el-card>
<edit-popup v-if="showEdit" ref="editRef" @success="getLists" @close="showEdit = false" />
</div>
</template>
<script lang="ts" setup name="admin">
import { usePaging } from '@/hooks/usePaging'
import feedback from '@/utils/feedback'
import EditPopup from './edit.vue'
import { OperationStatus, delOperation, getOperationList } from '@/api/setting/operation_manager'
const editRef = shallowRef<InstanceType<typeof EditPopup>>()
//
const formData = reactive({
name: '',
phone: '',
enable: 0
})
const showEdit = ref(false)
const { pager, getLists } = usePaging({
fetchFun: getOperationList,
params: formData
})
const handleAdd = async () => {
showEdit.value = true
await nextTick()
editRef.value?.open('add')
}
const handleEdit = async (data: any) => {
showEdit.value = true
await nextTick()
editRef.value?.open('edit')
editRef.value?.setFormData(data)
}
const handleDelete = async (id: number) => {
try {
await feedback.confirm('确定要删除?')
await delOperation({ id })
feedback.msgSuccess('删除成功')
getLists()
} catch (error) {}
}
const changeStatus = (row: any) => {
const { id, enable } = row
const msg = enable == 1 ? '停用' : '开启'
return new Promise(async resolve => {
try {
await feedback.confirm(`确定${msg}短信通知?`)
await OperationStatus({ id })
feedback.msgSuccess('修改成功')
getLists()
resolve(true)
} catch (error) {
getLists()
resolve(false)
}
})
}
onMounted(() => {
getLists()
})
</script>

View File

@ -1,66 +1,65 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>粤好生活-后台管理系统</title>
<style>
* {
margin: 0;
padding: 0;
}
.preload {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
.circular {
height: 42px;
width: 42px;
animation: loading-rotate 2s linear infinite;
}
.circular .path {
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90, 150;
stroke-dashoffset: 0;
stroke-width: 2;
stroke: #4073fa;
stroke-linecap: round;
}
@keyframes loading-rotate {
100% {
transform: rotate(1turn);
}
}
@keyframes loading-dash {
0% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
</style>
</head>
<body>
<div id="app">
<div class="preload">
<svg viewBox="25 25 50 50" class="circular">
<circle cx="50" cy="50" r="20" fill="none" class="path"></circle>
</svg>
</div>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>思缘生活-后台管理系统</title>
<style>
* {
margin: 0;
padding: 0;
}
.preload {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
.circular {
height: 42px;
width: 42px;
animation: loading-rotate 2s linear infinite;
}
.circular .path {
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90, 150;
stroke-dashoffset: 0;
stroke-width: 2;
stroke: #4073fa;
stroke-linecap: round;
}
@keyframes loading-rotate {
100% {
transform: rotate(1turn);
}
}
@keyframes loading-dash {
0% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
</style>
</head>
<body>
<div id="app">
<div class="preload">
<svg viewBox="25 25 50 50" class="circular">
<circle cx="50" cy="50" r="20" fill="none" class="path"></circle>
</svg>
</div>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>