mirror of
https://github.com/bufanyun/hotgo.git
synced 2025-08-28 09:33:52 +08:00
@@ -120,3 +120,4 @@
|
||||
<script src="/src/main.ts" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hotgo",
|
||||
"version": "2.5.3",
|
||||
"version": "2.6.7",
|
||||
"author": {
|
||||
"name": "MengShuai",
|
||||
"email": "133814250@qq.com",
|
||||
@@ -56,7 +56,8 @@
|
||||
"vue-router": "^4.0.15",
|
||||
"vue-types": "^4.1.1",
|
||||
"vue3-json-viewer": "^2.2.2",
|
||||
"vuedraggable": "^4.1.0"
|
||||
"vuedraggable": "^4.1.0",
|
||||
"weixin-js-sdk": "^1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^12.1.4",
|
||||
|
41
web/src/api/cash/index.ts
Normal file
41
web/src/api/cash/index.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
export function List(params) {
|
||||
return http.request({
|
||||
url: '/cash/list',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export function Edit(params) {
|
||||
return http.request({
|
||||
url: '/cash/edit',
|
||||
method: 'POST',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export function View(params) {
|
||||
return http.request({
|
||||
url: '/cash/view',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export function Apply(params) {
|
||||
return http.request({
|
||||
url: '/cash/apply',
|
||||
method: 'POST',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export function Payment(params) {
|
||||
return http.request({
|
||||
url: '/cash/payment',
|
||||
method: 'POST',
|
||||
params,
|
||||
});
|
||||
}
|
23
web/src/api/creditsLog/index.ts
Normal file
23
web/src/api/creditsLog/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { http, jumpExport } from '@/utils/http/axios';
|
||||
|
||||
// 获取资产变动列表
|
||||
export function List(params) {
|
||||
return http.request({
|
||||
url: '/creditsLog/list',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 导出资产变动
|
||||
export function Export(params) {
|
||||
jumpExport('/creditsLog/export', params);
|
||||
}
|
||||
|
||||
// 获取变动状态选项
|
||||
export function Option() {
|
||||
return http.request({
|
||||
url: '/creditsLog/option',
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
105
web/src/api/order/index.ts
Normal file
105
web/src/api/order/index.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { http, jumpExport } from '@/utils/http/axios';
|
||||
|
||||
// 创建充值订单
|
||||
export function Create(params) {
|
||||
return http.request({
|
||||
url: '/order/create',
|
||||
method: 'post',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 获取充值订单列表
|
||||
export function List(params) {
|
||||
return http.request({
|
||||
url: '/order/list',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 删除/批量删除充值订单
|
||||
export function Delete(params) {
|
||||
return http.request({
|
||||
url: '/order/delete',
|
||||
method: 'POST',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 添加/编辑充值订单
|
||||
export function Edit(params) {
|
||||
return http.request({
|
||||
url: '/order/edit',
|
||||
method: 'POST',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 修改充值订单状态
|
||||
export function Status(params) {
|
||||
return http.request({
|
||||
url: '/order/status',
|
||||
method: 'POST',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 操作充值订单开关
|
||||
export function Switch(params) {
|
||||
return http.request({
|
||||
url: '/order/switch',
|
||||
method: 'POST',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 获取充值订单指定详情
|
||||
export function View(params) {
|
||||
return http.request({
|
||||
url: '/order/view',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 获取充值订单最大排序
|
||||
export function MaxSort() {
|
||||
return http.request({
|
||||
url: '/order/maxSort',
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
// 获取订单状态选项
|
||||
export function Option() {
|
||||
return http.request({
|
||||
url: '/order/option',
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
// 申请订单退款
|
||||
export function ApplyRefund(params) {
|
||||
return http.request({
|
||||
url: '/order/applyRefund',
|
||||
method: 'post',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 受理订单退款
|
||||
export function AcceptRefund(params) {
|
||||
return http.request({
|
||||
url: '/order/acceptRefund',
|
||||
method: 'post',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 导出充值订单
|
||||
export function Export(params) {
|
||||
jumpExport('/order/export', params);
|
||||
}
|
@@ -31,3 +31,12 @@ export function Delete(params) {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export function getDeptOption() {
|
||||
const params = { pageSize: 100 };
|
||||
return http.request({
|
||||
url: '/dept/option',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
@@ -8,6 +8,10 @@ export function getPostList(params?) {
|
||||
});
|
||||
}
|
||||
|
||||
export function getPostOption(params?) {
|
||||
return getPostList(params);
|
||||
}
|
||||
|
||||
export function Edit(params) {
|
||||
return http.request({
|
||||
url: '/post/edit',
|
||||
|
@@ -47,3 +47,27 @@ export function GetMemberOption() {
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
export function GetMemberView(params) {
|
||||
return http.request({
|
||||
url: '/member/view',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export function AddMemberBalance(params) {
|
||||
return http.request({
|
||||
url: '/member/addBalance',
|
||||
method: 'POST',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export function AddMemberIntegral(params) {
|
||||
return http.request({
|
||||
url: '/member/addIntegral',
|
||||
method: 'POST',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
15
web/src/api/pay/refund.ts
Normal file
15
web/src/api/pay/refund.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { http, jumpExport } from '@/utils/http/axios';
|
||||
|
||||
// 获取交易退款列表
|
||||
export function List(params) {
|
||||
return http.request({
|
||||
url: '/payRefund/list',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 导出交易退款
|
||||
export function Export(params) {
|
||||
jumpExport('/payRefund/export', params);
|
||||
}
|
@@ -37,3 +37,10 @@ export function sendTestSms(params) {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export function getCashConfig() {
|
||||
return http.request({
|
||||
url: '/config/getCash',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
@@ -11,6 +11,11 @@ export function getRoleList(params) {
|
||||
});
|
||||
}
|
||||
|
||||
export function getRoleOption() {
|
||||
const params = { pageSize: 100 };
|
||||
return getRoleList(params);
|
||||
}
|
||||
|
||||
export function Edit(params) {
|
||||
return http.request({
|
||||
url: '/role/edit',
|
||||
|
@@ -3,6 +3,7 @@ export enum SocketEnum {
|
||||
EventConnected = 'connected',
|
||||
EventAdminMonitorTrends = 'admin/monitor/trends',
|
||||
EventAdminMonitorRunInfo = 'admin/monitor/runInfo',
|
||||
EventAdminOrderNotify = 'admin/order/notify',
|
||||
TypeQueryUser = 2,
|
||||
TypeBoardCastMsg = 3,
|
||||
TypeQuerySwitcher = 4,
|
||||
|
@@ -1,11 +1,13 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
import { isNavigationFailure, Router } from 'vue-router';
|
||||
import { useUserStoreWidthOut } from '@/store/modules/user';
|
||||
import { UserInfoState, useUserStoreWidthOut } from '@/store/modules/user';
|
||||
import { useAsyncRouteStoreWidthOut } from '@/store/modules/asyncRoute';
|
||||
import { ACCESS_TOKEN } from '@/store/mutation-types';
|
||||
import { storage } from '@/utils/Storage';
|
||||
import { PageEnum } from '@/enums/pageEnum';
|
||||
import { ErrorPageRoute } from '@/router/base';
|
||||
import { isWechatBrowser } from '@/utils/is';
|
||||
import { jump } from '@/utils/http/axios';
|
||||
|
||||
const LOGIN_PATH = PageEnum.BASE_LOGIN;
|
||||
const whitePathList = [LOGIN_PATH]; // no redirect whitelist
|
||||
@@ -55,7 +57,24 @@ export function createRouterGuards(router: Router) {
|
||||
return;
|
||||
}
|
||||
|
||||
const redirectPath = (from.query.redirect || to.path) as string;
|
||||
const redirect = decodeURIComponent(redirectPath);
|
||||
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };
|
||||
const userInfo = await userStore.GetInfo();
|
||||
|
||||
// 如果是微信访问,则记录本次登录的openid
|
||||
if (isWechatBrowser() && (userInfo as UserInfoState).openId === '') {
|
||||
let path = nextData.path;
|
||||
if (path === LOGIN_PATH) {
|
||||
path = PageEnum.BASE_HOME_REDIRECT;
|
||||
}
|
||||
|
||||
const w = window.location;
|
||||
const URI = w.protocol + '//' + w.host + w.pathname + '#' + path;
|
||||
jump('/wechat/authorize', { type: 'openId', syncRedirect: URI });
|
||||
return;
|
||||
}
|
||||
|
||||
await userStore.GetConfig();
|
||||
const routes = await asyncRouteStore.generateRoutes(userInfo);
|
||||
|
||||
@@ -70,9 +89,6 @@ export function createRouterGuards(router: Router) {
|
||||
router.addRoute(ErrorPageRoute as unknown as RouteRecordRaw);
|
||||
}
|
||||
|
||||
const redirectPath = (from.query.redirect || to.path) as string;
|
||||
const redirect = decodeURIComponent(redirectPath);
|
||||
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };
|
||||
asyncRouteStore.setDynamicAddedRoute(true);
|
||||
next(nextData);
|
||||
Loading && Loading.finish();
|
||||
|
@@ -4,7 +4,6 @@ import { store } from '@/store';
|
||||
import { ACCESS_TOKEN, CURRENT_CONFIG, CURRENT_USER, IS_LOCKSCREEN } from '@/store/mutation-types';
|
||||
import { ResultEnum } from '@/enums/httpEnum';
|
||||
import { getConfig, getUserInfo, login } from '@/api/system/user';
|
||||
|
||||
const Storage = createStorage({ storage: localStorage });
|
||||
|
||||
export interface UserInfoState {
|
||||
@@ -17,6 +16,7 @@ export interface UserInfoState {
|
||||
realName: string;
|
||||
avatar: string;
|
||||
balance: number;
|
||||
integral: number;
|
||||
sex: number;
|
||||
qq: string;
|
||||
email: string;
|
||||
@@ -33,6 +33,7 @@ export interface UserInfoState {
|
||||
loginCount: number;
|
||||
lastLoginAt: string;
|
||||
lastLoginIp: string;
|
||||
openId: string;
|
||||
}
|
||||
|
||||
export interface ConfigState {
|
||||
@@ -125,14 +126,13 @@ export const useUserStore = defineStore({
|
||||
return Promise.reject(e);
|
||||
}
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
GetInfo() {
|
||||
const that: any = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
getUserInfo()
|
||||
.then((res) => {
|
||||
const result = res;
|
||||
const result = res as UserInfoState;
|
||||
if (result.permissions && result.permissions.length) {
|
||||
const permissionsList = result.permissions;
|
||||
that.setPermissions(permissionsList);
|
||||
@@ -143,7 +143,7 @@ export const useUserStore = defineStore({
|
||||
} else {
|
||||
reject(new Error('getInfo: permissionsList must be a non-null array !'));
|
||||
}
|
||||
resolve(res);
|
||||
resolve(result);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
|
@@ -178,11 +178,15 @@ export function defShortcuts() {
|
||||
|
||||
export function defRangeShortcuts() {
|
||||
const nowDate = new Date();
|
||||
const dayBase = 86400 * 1000;
|
||||
return {
|
||||
今天: [startOfToday().getTime(), endOfToday().getTime()] as const,
|
||||
昨天: () => {
|
||||
return [startOfYesterday().getTime(), endOfYesterday().getTime()] as const;
|
||||
},
|
||||
近7天: [startOfToday().getTime() - dayBase * 6, endOfToday().getTime()] as const,
|
||||
近30天: [startOfToday().getTime() - dayBase * 29, endOfToday().getTime()] as const,
|
||||
近90天: [startOfToday().getTime() - dayBase * 89, endOfToday().getTime()] as const,
|
||||
本周: () => {
|
||||
return [
|
||||
startOfWeek(nowDate, { weekStartsOn: 1 }).getTime(),
|
||||
@@ -192,7 +196,7 @@ export function defRangeShortcuts() {
|
||||
本月: () => {
|
||||
return [startOfMonth(nowDate).getTime(), endOfMonth(nowDate).getTime()] as const;
|
||||
},
|
||||
上个月: () => {
|
||||
上月: () => {
|
||||
return [
|
||||
startOfMonth(subMonths(nowDate, 1)).getTime(),
|
||||
endOfMonth(subMonths(nowDate, 1)).getTime(),
|
||||
|
@@ -19,6 +19,7 @@ import { useUserStoreWidthOut } from '@/store/modules/user';
|
||||
import router from '@/router';
|
||||
import { storage } from '@/utils/Storage';
|
||||
import { encodeParams } from '@/utils/urlUtils';
|
||||
import { delNullProperty } from '@/utils/array';
|
||||
|
||||
const globSetting = useGlobSetting();
|
||||
const urlPrefix = globSetting.urlPrefix || '';
|
||||
@@ -287,7 +288,22 @@ export const jumpExport = function (url, params) {
|
||||
urlPrefix +
|
||||
url +
|
||||
'?' +
|
||||
encodeParams({ ...params, ...{ authorization: useUserStoreWidthOut().token } });
|
||||
encodeParams({
|
||||
...delNullProperty(params),
|
||||
...{ authorization: useUserStoreWidthOut().token },
|
||||
});
|
||||
};
|
||||
|
||||
// 跳转
|
||||
export const jump = function (url, params) {
|
||||
window.location.href =
|
||||
urlPrefix +
|
||||
url +
|
||||
'?' +
|
||||
encodeParams({
|
||||
...delNullProperty(params),
|
||||
...{ authorization: useUserStoreWidthOut().token },
|
||||
});
|
||||
};
|
||||
|
||||
// 项目,多个不同 api 地址,直接在这里导出多个
|
||||
|
@@ -142,3 +142,8 @@ export function isLetterBegin(str) {
|
||||
export function isUrl(url: string): boolean {
|
||||
return /(^http|https:\/\/)/g.test(url);
|
||||
}
|
||||
|
||||
// 判断是否为微信浏览器
|
||||
export function isWechatBrowser(): boolean {
|
||||
return /micromessenger/.test(navigator.userAgent.toLowerCase()) ? true : false;
|
||||
}
|
||||
|
@@ -106,4 +106,4 @@ async function loadOptions() {
|
||||
});
|
||||
}
|
||||
|
||||
await loadOptions();
|
||||
await loadOptions();
|
||||
|
106
web/src/views/asset/cash/columns.ts
Normal file
106
web/src/views/asset/cash/columns.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { h } from 'vue';
|
||||
import { NTag } from 'naive-ui';
|
||||
|
||||
const msgMap = {
|
||||
1: '处理中',
|
||||
2: '提现成功',
|
||||
3: ' 提现异常',
|
||||
};
|
||||
|
||||
export const statusOptions = [
|
||||
{
|
||||
value: 1,
|
||||
label: '处理中',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '提现成功',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '提现异常',
|
||||
},
|
||||
];
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: '提现ID',
|
||||
key: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
key: 'memberUser',
|
||||
render(row) {
|
||||
return row.memberUser;
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '姓名',
|
||||
key: 'memberName',
|
||||
render(row) {
|
||||
return row.memberName;
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '提现金额',
|
||||
key: 'money',
|
||||
render(row) {
|
||||
return row.money.toFixed(2);
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '手续费',
|
||||
key: 'fee',
|
||||
render(row) {
|
||||
return row.fee.toFixed(2);
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '最终到账',
|
||||
key: 'lastMoney',
|
||||
render(row) {
|
||||
return row.lastMoney.toFixed(2);
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '处理结果',
|
||||
key: 'msg',
|
||||
render(row) {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
style: {
|
||||
marginRight: '6px',
|
||||
},
|
||||
type: row.status == 1 ? 'info' : row.status == 2 ? 'success' : 'warning',
|
||||
bordered: false,
|
||||
},
|
||||
{
|
||||
default: () => (row.msg == '' ? msgMap[row.status] : row.msg),
|
||||
}
|
||||
);
|
||||
},
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '申请IP',
|
||||
key: 'ip',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '处理时间',
|
||||
key: 'handleAt',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '申请时间',
|
||||
key: 'createdAt',
|
||||
width: 180,
|
||||
},
|
||||
];
|
41
web/src/views/asset/cash/index.vue
Normal file
41
web/src/views/asset/cash/index.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="提现管理" />
|
||||
</div>
|
||||
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<n-tabs
|
||||
type="card"
|
||||
class="card-tabs"
|
||||
:value="defaultTab"
|
||||
animated
|
||||
@before-leave="handleBeforeLeave"
|
||||
>
|
||||
<n-tab-pane name="" tab="全部"> <List :type="defaultTab" /></n-tab-pane>
|
||||
<n-tab-pane name="1" tab="处理中"> <List :type="defaultTab" /> </n-tab-pane>
|
||||
<n-tab-pane name="2" tab="提现成功"> <List :type="defaultTab" /> </n-tab-pane>
|
||||
<n-tab-pane name="3" tab="提现异常"> <List :type="defaultTab" /> </n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import List from './list.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
const defaultTab = ref('');
|
||||
|
||||
onMounted(() => {
|
||||
if (router.currentRoute.value.query?.type) {
|
||||
defaultTab.value = router.currentRoute.value.query.type as string;
|
||||
}
|
||||
});
|
||||
|
||||
function handleBeforeLeave(tabName: string) {
|
||||
defaultTab.value = tabName;
|
||||
}
|
||||
</script>
|
415
web/src/views/asset/cash/list.vue
Normal file
415
web/src/views/asset/cash/list.vue
Normal file
@@ -0,0 +1,415 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<BasicForm
|
||||
@register="register"
|
||||
@submit="handleSubmit"
|
||||
@reset="handleReset"
|
||||
@keyup.enter="handleSubmit"
|
||||
ref="searchFormRef"
|
||||
>
|
||||
<template #statusSlot="{ model, field }">
|
||||
<n-input v-model:value="model[field]" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
|
||||
<BasicTable
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
:actionColumn="actionColumn"
|
||||
:scroll-x="1800"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<n-button type="primary" @click="addTable" class="min-left-space">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<MoneyCollectOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
申请提现
|
||||
</n-button>
|
||||
|
||||
<n-button type="default" @click="setCash" class="min-left-space">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<EditOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
设置提现账户
|
||||
</n-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<n-modal
|
||||
v-model:show="showModal"
|
||||
:show-icon="false"
|
||||
preset="dialog"
|
||||
title="申请提现"
|
||||
:style="{
|
||||
width: dialogWidth,
|
||||
}"
|
||||
>
|
||||
<n-alert type="info">
|
||||
<div v-html="config.cashTips"></div>
|
||||
</n-alert>
|
||||
<n-form
|
||||
:model="formParams"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
label-placement="left"
|
||||
:label-width="100"
|
||||
class="py-4"
|
||||
>
|
||||
<n-form-item label="可提现金额">
|
||||
<n-input v-model:value="newUserInfo.balance" disabled />
|
||||
<template #feedback
|
||||
><p>{{ estimated }}</p></template
|
||||
>
|
||||
</n-form-item>
|
||||
<br />
|
||||
<n-form-item label="提现金额" path="money">
|
||||
<n-input-number v-model:value="formParams.money" :min="1" :max="newUserInfo.balance">
|
||||
<template #minus-icon>
|
||||
<n-icon :component="ArrowDownCircleOutline" />
|
||||
</template>
|
||||
<template #add-icon>
|
||||
<n-icon :component="ArrowUpCircleOutline" />
|
||||
</template>
|
||||
</n-input-number>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="() => (showModal = false)">取消</n-button>
|
||||
<n-button type="info" :loading="formBtnLoading" @click="confirmForm">确定</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
|
||||
<n-modal
|
||||
v-model:show="showPaymentModal"
|
||||
:show-icon="false"
|
||||
preset="dialog"
|
||||
title="处理打款"
|
||||
:style="{
|
||||
width: dialogWidth,
|
||||
}"
|
||||
>
|
||||
<n-form
|
||||
:model="paymentParams"
|
||||
:rules="rules"
|
||||
ref="PaymentRef"
|
||||
label-placement="left"
|
||||
:label-width="100"
|
||||
class="py-4"
|
||||
>
|
||||
<n-form-item label="最终到账金额">
|
||||
<n-input v-model:value="paymentParams.lastMoney" disabled />
|
||||
</n-form-item>
|
||||
<n-form-item label="收款信息">
|
||||
<n-input v-model:value="paymentParams.accountInfo" disabled />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="收款码">
|
||||
<n-carousel draggable>
|
||||
<img style="width: 200px" class="carousel-img" :src="paymentParams.payeeCode" />
|
||||
</n-carousel>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="提现状态" path="status">
|
||||
<n-radio-group v-model:value="paymentParams.status" name="status">
|
||||
<n-radio-button
|
||||
v-for="status in statusOptions"
|
||||
:key="status.value"
|
||||
:value="status.value"
|
||||
:label="status.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="处理结果">
|
||||
<n-input v-model:value="paymentParams.msg" />
|
||||
<template #feedback>不填默认显示提现状态</template>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="() => (showPaymentModal = false)">取消</n-button>
|
||||
<n-button type="info" :loading="PaymentBtnLoading" @click="confirmPayment"
|
||||
>确定</n-button
|
||||
>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { h, reactive, ref } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { BasicForm, FormSchema, useForm } from '@/components/Form/index';
|
||||
import { Apply, List, Payment, View } from '@/api/cash';
|
||||
import { columns, statusOptions } from './columns';
|
||||
import { ArrowDownCircleOutline, ArrowUpCircleOutline } from '@vicons/ionicons5';
|
||||
import { MoneyCollectOutlined, EditOutlined } from '@vicons/antd';
|
||||
import { defRangeShortcuts, timestampToTime } from '@/utils/dateUtil';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { getUserInfo } from '@/api/system/user';
|
||||
import { getCashConfig } from '@/api/sys/config';
|
||||
|
||||
interface Props {
|
||||
type?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
type: '',
|
||||
});
|
||||
const router = useRouter();
|
||||
|
||||
const params = ref<any>({
|
||||
pageSize: 10,
|
||||
title: '',
|
||||
content: '',
|
||||
status: null,
|
||||
});
|
||||
|
||||
const rules = {};
|
||||
|
||||
const estimated = ref(
|
||||
'本次提现预计将在 ' +
|
||||
timestampToTime(new Date().setTime(new Date().getTime() + 86400 * 4 * 1000) / 1000) +
|
||||
' 前到账 (1-3个工作日,双休日和法定节假日顺延)'
|
||||
);
|
||||
|
||||
const schemas: FormSchema[] = [
|
||||
{
|
||||
field: 'memberId',
|
||||
component: 'NInput',
|
||||
label: '管理员ID',
|
||||
componentProps: {
|
||||
placeholder: '请输入管理员ID',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
rules: [{ message: '请输入管理员ID', trigger: ['blur'] }],
|
||||
},
|
||||
{
|
||||
field: 'ip',
|
||||
component: 'NInput',
|
||||
label: '申请IP',
|
||||
componentProps: {
|
||||
placeholder: '请输入申请IP',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
rules: [{ message: '请输入申请IP', trigger: ['blur'] }],
|
||||
},
|
||||
{
|
||||
field: 'created_at',
|
||||
component: 'NDatePicker',
|
||||
label: '申请时间',
|
||||
componentProps: {
|
||||
type: 'datetimerange',
|
||||
clearable: true,
|
||||
shortcuts: defRangeShortcuts(),
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const newUserInfo = ref({ balance: 0 });
|
||||
const message = useMessage();
|
||||
const actionRef = ref();
|
||||
const showModal = ref(false);
|
||||
const showPaymentModal = ref(false);
|
||||
const PaymentRef = ref<any>({});
|
||||
const PaymentBtnLoading = ref(false);
|
||||
const formBtnLoading = ref(false);
|
||||
const searchFormRef = ref<any>({});
|
||||
const formRef = ref<any>({});
|
||||
const config = ref<any>({
|
||||
cashMinFee: 3,
|
||||
cashMinFeeRatio: '0.03',
|
||||
cashMinMoney: 0,
|
||||
cashSwitch: false,
|
||||
cashTips: '',
|
||||
});
|
||||
|
||||
const resetFormParams = {
|
||||
money: null,
|
||||
accountInfo: null,
|
||||
};
|
||||
let formParams = ref<any>(resetFormParams);
|
||||
|
||||
const resetPaymentParams = {
|
||||
id: null,
|
||||
money: null,
|
||||
};
|
||||
let paymentParams = ref<any>(resetPaymentParams);
|
||||
|
||||
const actionColumn = reactive({
|
||||
auth: ['/cash/payment'],
|
||||
width: 100,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
render(record) {
|
||||
return h(TableAction as any, {
|
||||
style: 'button',
|
||||
actions: [
|
||||
{
|
||||
label: '处理打款',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function setCash() {
|
||||
router.push({
|
||||
name: 'home_account',
|
||||
query: {
|
||||
type: 3,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const [register, {}] = useForm({
|
||||
gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
|
||||
labelWidth: 80,
|
||||
schemas,
|
||||
});
|
||||
|
||||
async function addTable() {
|
||||
showModal.value = true;
|
||||
formParams.value = resetFormParams;
|
||||
newUserInfo.value = await getUserInfo();
|
||||
|
||||
if (newUserInfo.value.balance < config.value.cashMinMoney) {
|
||||
message.error('当前余额不满足提现条件,至少需要:' + config.value.cashMinMoney + '元');
|
||||
}
|
||||
}
|
||||
|
||||
const loadDataTable = async (res) => {
|
||||
mapWidth();
|
||||
config.value = await getCashConfig();
|
||||
config.value = config.value.list;
|
||||
return await List({
|
||||
...params.value,
|
||||
...res,
|
||||
...searchFormRef.value.formModel,
|
||||
...{ status: props.type },
|
||||
});
|
||||
};
|
||||
|
||||
function reloadTable() {
|
||||
actionRef.value.reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请提现
|
||||
* @param e
|
||||
*/
|
||||
function confirmForm(e) {
|
||||
e.preventDefault();
|
||||
formBtnLoading.value = true;
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
Apply({ money: formParams.value.money })
|
||||
.then((_res) => {
|
||||
message.success('操作成功');
|
||||
setTimeout(() => {
|
||||
showModal.value = false;
|
||||
reloadTable();
|
||||
formParams.value = ref(resetFormParams);
|
||||
});
|
||||
})
|
||||
.catch((_e: Error) => {
|
||||
// message.error(e.message ?? '操作失败');
|
||||
});
|
||||
} else {
|
||||
message.error('请填写完整信息');
|
||||
}
|
||||
formBtnLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理打款
|
||||
* @param e
|
||||
*/
|
||||
function confirmPayment(e) {
|
||||
e.preventDefault();
|
||||
PaymentBtnLoading.value = true;
|
||||
PaymentRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
Payment({
|
||||
id: PaymentRef.value.model.id,
|
||||
status: PaymentRef.value.model.status,
|
||||
msg: PaymentRef.value.model.msg,
|
||||
})
|
||||
.then((_res) => {
|
||||
message.success('操作成功');
|
||||
setTimeout(() => {
|
||||
showPaymentModal.value = false;
|
||||
reloadTable();
|
||||
PaymentRef.value = ref(resetPaymentParams);
|
||||
});
|
||||
})
|
||||
.catch((_e: Error) => {
|
||||
// message.error(e.message ?? '操作失败');
|
||||
});
|
||||
} else {
|
||||
message.error('请填写完整信息');
|
||||
}
|
||||
PaymentBtnLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
async function handleEdit(record: Recordable) {
|
||||
showPaymentModal.value = true;
|
||||
paymentParams.value = record;
|
||||
paymentParams.value = await View({ id: record.id });
|
||||
paymentParams.value.lastMoney = paymentParams.value.lastMoney.toFixed(2);
|
||||
paymentParams.value.accountInfo =
|
||||
paymentParams.value.name + ' - ' + paymentParams.value.account;
|
||||
}
|
||||
|
||||
function handleSubmit(values: Recordable) {
|
||||
console.log(values);
|
||||
params.value = values;
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
function handleReset(values: Recordable) {
|
||||
params.value = values;
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
const dialogWidth = ref('50%');
|
||||
|
||||
function mapWidth() {
|
||||
let val = document.body.clientWidth;
|
||||
const def = 720; // 默认宽度
|
||||
if (val < def) {
|
||||
dialogWidth.value = '100%';
|
||||
} else {
|
||||
dialogWidth.value = def + 'px';
|
||||
}
|
||||
|
||||
return dialogWidth.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
44
web/src/views/asset/creditsLog/index.vue
Normal file
44
web/src/views/asset/creditsLog/index.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="资金变动">
|
||||
你和下级在平台中余额、积分的变动明细都可以在这里进行查看
|
||||
</n-card>
|
||||
</div>
|
||||
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<n-tabs
|
||||
type="card"
|
||||
class="card-tabs"
|
||||
:value="defaultTab"
|
||||
animated
|
||||
@before-leave="handleBeforeLeave"
|
||||
>
|
||||
<n-tab-pane name="" tab="全部"> <List :type="defaultTab" /></n-tab-pane>
|
||||
<n-tab-pane name="balance" tab="余额明细"> <List :type="defaultTab" /> </n-tab-pane>
|
||||
<n-tab-pane name="integral" tab="积分明细"> <List :type="defaultTab" /> </n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import List from './list.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
const defaultTab = ref('');
|
||||
|
||||
onMounted(() => {
|
||||
if (router.currentRoute.value.query?.type) {
|
||||
defaultTab.value = router.currentRoute.value.query.type as string;
|
||||
}
|
||||
});
|
||||
|
||||
function handleBeforeLeave(tabName: string) {
|
||||
defaultTab.value = tabName;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
93
web/src/views/asset/creditsLog/list.vue
Normal file
93
web/src/views/asset/creditsLog/list.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<BasicForm
|
||||
@register="register"
|
||||
@submit="reloadTable"
|
||||
@reset="reloadTable"
|
||||
@keyup.enter="reloadTable"
|
||||
ref="searchFormRef"
|
||||
>
|
||||
<template #statusSlot="{ model, field }">
|
||||
<n-input v-model:value="model[field]" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
|
||||
<BasicTable
|
||||
:openChecked="false"
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
:scroll-x="1800"
|
||||
:resizeHeightOffset="-10000"
|
||||
size="small"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<n-button
|
||||
type="primary"
|
||||
@click="handleExport"
|
||||
class="min-left-space"
|
||||
v-if="hasPermission(['/creditsLog/export'])"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<ExportOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
导出
|
||||
</n-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { BasicTable } from '@/components/Table';
|
||||
import { BasicForm, useForm } from '@/components/Form/index';
|
||||
import { usePermission } from '@/hooks/web/usePermission';
|
||||
import { List, Export } from '@/api/creditsLog';
|
||||
import { columns, schemas } from './model';
|
||||
import { ExportOutlined } from '@vicons/antd';
|
||||
|
||||
interface Props {
|
||||
type?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
type: '',
|
||||
});
|
||||
|
||||
const { hasPermission } = usePermission();
|
||||
const actionRef = ref();
|
||||
const message = useMessage();
|
||||
const searchFormRef = ref<any>({});
|
||||
|
||||
const [register, {}] = useForm({
|
||||
gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
|
||||
labelWidth: 80,
|
||||
schemas,
|
||||
});
|
||||
|
||||
const loadDataTable = async (res) => {
|
||||
return await List({
|
||||
...searchFormRef.value?.formModel,
|
||||
...res,
|
||||
...{ creditType: props.type },
|
||||
});
|
||||
};
|
||||
|
||||
function reloadTable() {
|
||||
actionRef.value.reload();
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
message.loading('正在导出列表...', { duration: 1200 });
|
||||
Export(searchFormRef.value?.formModel);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
256
web/src/views/asset/creditsLog/model.ts
Normal file
256
web/src/views/asset/creditsLog/model.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
import { h, ref } from 'vue';
|
||||
import { NTag } from 'naive-ui';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { FormSchema } from '@/components/Form';
|
||||
import { Option } from '@/api/creditsLog';
|
||||
import { isNullObject } from '@/utils/is';
|
||||
import { defRangeShortcuts } from '@/utils/dateUtil';
|
||||
import { getOptionLabel, getOptionTag, Options } from '@/utils/hotgo';
|
||||
|
||||
export interface State {
|
||||
id: number;
|
||||
memberId: number;
|
||||
appId: string;
|
||||
addonsName: string;
|
||||
creditType: string;
|
||||
creditGroup: string;
|
||||
beforeNum: number;
|
||||
num: number;
|
||||
afterNum: number;
|
||||
remark: string;
|
||||
ip: string;
|
||||
mapId: number;
|
||||
status: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export const defaultState = {
|
||||
id: 0,
|
||||
memberId: 0,
|
||||
appId: '',
|
||||
addonsName: '',
|
||||
creditType: '',
|
||||
creditGroup: '',
|
||||
beforeNum: 0,
|
||||
num: 0,
|
||||
afterNum: 0,
|
||||
remark: '',
|
||||
ip: '',
|
||||
mapId: 0,
|
||||
status: 1,
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
};
|
||||
|
||||
export function newState(state: State | null): State {
|
||||
if (state !== null) {
|
||||
return cloneDeep(state);
|
||||
}
|
||||
return cloneDeep(defaultState);
|
||||
}
|
||||
|
||||
export const options = ref<Options>({
|
||||
creditType: [],
|
||||
creditGroup: [],
|
||||
});
|
||||
|
||||
export const rules = {};
|
||||
|
||||
export const schemas = ref<FormSchema[]>([
|
||||
{
|
||||
field: 'memberId',
|
||||
component: 'NInput',
|
||||
label: '管理员ID',
|
||||
componentProps: {
|
||||
placeholder: '请输入管理员ID',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'creditGroup',
|
||||
component: 'NSelect',
|
||||
label: '组别',
|
||||
defaultValue: null,
|
||||
componentProps: {
|
||||
placeholder: '请选择变动的组别',
|
||||
options: [],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
component: 'NInput',
|
||||
label: '备注',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'ip',
|
||||
component: 'NInput',
|
||||
label: '操作人IP',
|
||||
componentProps: {
|
||||
placeholder: '请输入操作人IP',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createdAt',
|
||||
component: 'NDatePicker',
|
||||
label: '变动时间',
|
||||
componentProps: {
|
||||
type: 'datetimerange',
|
||||
clearable: true,
|
||||
shortcuts: defRangeShortcuts(),
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'id',
|
||||
component: 'NInput',
|
||||
label: '变动ID',
|
||||
componentProps: {
|
||||
placeholder: '请输入变动ID',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: '变动ID',
|
||||
key: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '管理员ID',
|
||||
key: 'memberId',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '变动类型',
|
||||
key: 'creditType',
|
||||
render(row) {
|
||||
if (isNullObject(row.creditType)) {
|
||||
return ``;
|
||||
}
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
style: {
|
||||
marginRight: '6px',
|
||||
},
|
||||
type: getOptionTag(options.value.creditType, row.creditType),
|
||||
bordered: false,
|
||||
},
|
||||
{
|
||||
default: () => getOptionLabel(options.value.creditType, row.creditType),
|
||||
}
|
||||
);
|
||||
},
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '组别',
|
||||
key: 'creditGroup',
|
||||
render(row) {
|
||||
if (isNullObject(row.creditGroup)) {
|
||||
return ``;
|
||||
}
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
style: {
|
||||
marginRight: '6px',
|
||||
},
|
||||
type: getOptionTag(options.value.creditGroup, row.creditGroup),
|
||||
bordered: false,
|
||||
},
|
||||
{
|
||||
default: () => getOptionLabel(options.value.creditGroup, row.creditGroup),
|
||||
}
|
||||
);
|
||||
},
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '变动前',
|
||||
key: 'beforeNum',
|
||||
width: 100,
|
||||
render(row) {
|
||||
return Number(row.beforeNum).toFixed(2);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '变动数量',
|
||||
key: 'num',
|
||||
width: 100,
|
||||
render(row) {
|
||||
return Number(row.num).toFixed(2);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '变动后',
|
||||
key: 'afterNum',
|
||||
width: 100,
|
||||
render(row) {
|
||||
return Number(row.afterNum).toFixed(2);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
key: 'remark',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '操作人IP',
|
||||
key: 'ip',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '关联ID',
|
||||
key: 'mapId',
|
||||
width: 100,
|
||||
render(row) {
|
||||
if (row.mapId === 0) {
|
||||
return '-';
|
||||
}
|
||||
return row.mapId;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '变动时间',
|
||||
key: 'createdAt',
|
||||
width: 180,
|
||||
},
|
||||
];
|
||||
|
||||
async function loadOptions() {
|
||||
options.value = await Option();
|
||||
for (const item of schemas.value) {
|
||||
switch (item.field) {
|
||||
case 'creditType':
|
||||
item.componentProps.options = options.value.creditType;
|
||||
break;
|
||||
case 'creditGroup':
|
||||
item.componentProps.options = options.value.creditGroup;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await loadOptions();
|
101
web/src/views/asset/payRefund/index.vue
Normal file
101
web/src/views/asset/payRefund/index.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="交易退款">
|
||||
<!-- 这是系统自动生成的CURD表格,你可以将此行注释改为表格的描述 -->
|
||||
</n-card>
|
||||
</div>
|
||||
<BasicForm
|
||||
@register="register"
|
||||
@submit="reloadTable"
|
||||
@reset="reloadTable"
|
||||
@keyup.enter="reloadTable"
|
||||
ref="searchFormRef"
|
||||
>
|
||||
<template #statusSlot="{ model, field }">
|
||||
<n-input v-model:value="model[field]" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
|
||||
<BasicTable
|
||||
:openChecked="false"
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
:actionColumn="actionColumn"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
:scroll-x="1090"
|
||||
:resizeHeightOffset="-10000"
|
||||
size="small"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<n-button
|
||||
type="primary"
|
||||
@click="handleExport"
|
||||
class="min-left-space"
|
||||
v-if="hasPermission(['/creditsLog/export'])"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<ExportOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
导出
|
||||
</n-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { h, reactive, ref } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { BasicForm, useForm } from '@/components/Form/index';
|
||||
import { usePermission } from '@/hooks/web/usePermission';
|
||||
import { List, Export } from '@/api/pay/refund';
|
||||
import { columns, schemas } from './model';
|
||||
import { ExportOutlined } from '@vicons/antd';
|
||||
|
||||
const { hasPermission } = usePermission();
|
||||
const actionRef = ref();
|
||||
const message = useMessage();
|
||||
const searchFormRef = ref<any>({});
|
||||
|
||||
const actionColumn = reactive({
|
||||
width: 300,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
// fixed: 'right',
|
||||
render(record) {
|
||||
return h(TableAction as any, {
|
||||
style: 'button',
|
||||
actions: [],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const [register, {}] = useForm({
|
||||
gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
|
||||
labelWidth: 80,
|
||||
schemas,
|
||||
});
|
||||
|
||||
const loadDataTable = async (res) => {
|
||||
return await List({ ...searchFormRef.value?.formModel, ...res });
|
||||
};
|
||||
|
||||
function reloadTable() {
|
||||
actionRef.value.reload();
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
message.loading('正在导出列表...', { duration: 1200 });
|
||||
Export(searchFormRef.value?.formModel);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
255
web/src/views/asset/payRefund/model.ts
Normal file
255
web/src/views/asset/payRefund/model.ts
Normal file
@@ -0,0 +1,255 @@
|
||||
import { h, ref } from 'vue';
|
||||
import { NAvatar, NImage, NTag, NSwitch, NRate } from 'naive-ui';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { FormSchema } from '@/components/Form';
|
||||
import { Dicts } from '@/api/dict/dict';
|
||||
import { isNullObject } from '@/utils/is';
|
||||
import { defRangeShortcuts } from '@/utils/dateUtil';
|
||||
import { getOptionLabel, getOptionTag, Options } from '@/utils/hotgo';
|
||||
|
||||
export interface State {
|
||||
id: number;
|
||||
memberId: number;
|
||||
appId: string;
|
||||
addonsName: string;
|
||||
creditType: string;
|
||||
creditGroup: string;
|
||||
beforeNum: number;
|
||||
num: number;
|
||||
afterNum: number;
|
||||
remark: string;
|
||||
ip: string;
|
||||
mapId: number;
|
||||
status: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export const defaultState = {
|
||||
id: 0,
|
||||
memberId: 0,
|
||||
appId: '',
|
||||
addonsName: '',
|
||||
creditType: '',
|
||||
creditGroup: '',
|
||||
beforeNum: 0,
|
||||
num: 0,
|
||||
afterNum: 0,
|
||||
remark: '',
|
||||
ip: '',
|
||||
mapId: 0,
|
||||
status: 1,
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
};
|
||||
|
||||
export function newState(state: State | null): State {
|
||||
if (state !== null) {
|
||||
return cloneDeep(state);
|
||||
}
|
||||
return cloneDeep(defaultState);
|
||||
}
|
||||
|
||||
export const options = ref<Options>({
|
||||
sys_normal_disable: [],
|
||||
});
|
||||
|
||||
export const rules = {};
|
||||
|
||||
export const schemas = ref<FormSchema[]>([
|
||||
{
|
||||
field: 'id',
|
||||
component: 'NInputNumber',
|
||||
label: '变动ID',
|
||||
componentProps: {
|
||||
placeholder: '请输入变动ID',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'memberId',
|
||||
component: 'NInputNumber',
|
||||
label: '管理员ID',
|
||||
componentProps: {
|
||||
placeholder: '请输入管理员ID',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'appId',
|
||||
component: 'NInput',
|
||||
label: '应用id',
|
||||
componentProps: {
|
||||
placeholder: '请输入应用id',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'creditType',
|
||||
component: 'NSelect',
|
||||
label: '变动类型',
|
||||
defaultValue: null,
|
||||
componentProps: {
|
||||
placeholder: '请选择变动类型',
|
||||
options: [],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'creditGroup',
|
||||
component: 'NSelect',
|
||||
label: '变动的组别',
|
||||
defaultValue: null,
|
||||
componentProps: {
|
||||
placeholder: '请选择变动的组别',
|
||||
options: [],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
component: 'NInput',
|
||||
label: '备注',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'ip',
|
||||
component: 'NInput',
|
||||
label: '操作人IP',
|
||||
componentProps: {
|
||||
placeholder: '请输入操作人IP',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
component: 'NSelect',
|
||||
label: '状态',
|
||||
defaultValue: null,
|
||||
componentProps: {
|
||||
placeholder: '请选择状态',
|
||||
options: [],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createdAt',
|
||||
component: 'NDatePicker',
|
||||
label: '创建时间',
|
||||
componentProps: {
|
||||
type: 'datetimerange',
|
||||
clearable: true,
|
||||
shortcuts: defRangeShortcuts(),
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: '变动ID',
|
||||
key: 'id',
|
||||
},
|
||||
{
|
||||
title: '管理员ID',
|
||||
key: 'memberId',
|
||||
},
|
||||
{
|
||||
title: '应用id',
|
||||
key: 'appId',
|
||||
},
|
||||
{
|
||||
title: '插件名称',
|
||||
key: 'addonsName',
|
||||
},
|
||||
{
|
||||
title: '变动前',
|
||||
key: 'beforeNum',
|
||||
},
|
||||
{
|
||||
title: '变动数据',
|
||||
key: 'num',
|
||||
},
|
||||
{
|
||||
title: '变动后',
|
||||
key: 'afterNum',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
key: 'remark',
|
||||
},
|
||||
{
|
||||
title: '操作人IP',
|
||||
key: 'ip',
|
||||
},
|
||||
{
|
||||
title: '关联ID',
|
||||
key: 'mapId',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
render(row) {
|
||||
if (isNullObject(row.status)) {
|
||||
return ``;
|
||||
}
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
style: {
|
||||
marginRight: '6px',
|
||||
},
|
||||
type: getOptionTag(options.value.sys_normal_disable, row.status),
|
||||
bordered: false,
|
||||
},
|
||||
{
|
||||
default: () => getOptionLabel(options.value.sys_normal_disable, row.status),
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'createdAt',
|
||||
},
|
||||
{
|
||||
title: '修改时间',
|
||||
key: 'updatedAt',
|
||||
},
|
||||
];
|
||||
|
||||
async function loadOptions() {
|
||||
options.value = await Dicts({
|
||||
types: ['sys_normal_disable'],
|
||||
});
|
||||
for (const item of schemas.value) {
|
||||
switch (item.field) {
|
||||
case 'status':
|
||||
item.componentProps.options = options.value.sys_normal_disable;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await loadOptions();
|
287
web/src/views/asset/recharge/recharge.vue
Normal file
287
web/src/views/asset/recharge/recharge.vue
Normal file
@@ -0,0 +1,287 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="在线充值"> 余额可用于购买付费产品或商城消费 </n-card>
|
||||
</div>
|
||||
<n-spin :show="loading">
|
||||
<n-grid class="mt-6" cols="1 s:1 m:1 l:4 xl:4 2xl:4" responsive="screen" :x-gap="12">
|
||||
<n-gi span="4">
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<n-thing>
|
||||
<template #description> <span class="title">充值金额</span> </template>
|
||||
<n-space>
|
||||
<n-button
|
||||
type="primary"
|
||||
ghost
|
||||
v-for="item in amounts"
|
||||
:key="item"
|
||||
@click="SetAmount(item)"
|
||||
>
|
||||
¥{{ item }}
|
||||
|
||||
<n-icon
|
||||
class="check-icon"
|
||||
:size="18"
|
||||
:component="CheckOutlined"
|
||||
v-if="amount === item && amountType === 1"
|
||||
/>
|
||||
</n-button>
|
||||
|
||||
<n-input-number v-model:value="amount" v-if="amountType === 2">
|
||||
<template #prefix> ¥ </template>
|
||||
</n-input-number>
|
||||
</n-space>
|
||||
<template #footer> <span class="title">支付方式 </span></template>
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button
|
||||
strong
|
||||
secondary
|
||||
:color="item.color"
|
||||
v-for="item in payTypes"
|
||||
:key="item"
|
||||
@click="SetPayType(item.value)"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon :component="item.icon" />
|
||||
</template>
|
||||
{{ item.label }}
|
||||
<n-icon
|
||||
class="check-icon"
|
||||
:size="18"
|
||||
:component="CheckOutlined"
|
||||
v-if="payType === item.value"
|
||||
/>
|
||||
</n-button>
|
||||
</n-space>
|
||||
|
||||
<n-button
|
||||
type="success"
|
||||
class="create-order-button"
|
||||
size="large"
|
||||
@click="CreateOrder"
|
||||
>
|
||||
立即充值
|
||||
</n-button>
|
||||
</template>
|
||||
</n-thing>
|
||||
</n-card>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
</n-spin>
|
||||
|
||||
<n-modal v-model:show="showQrModal" :show-icon="false" preset="dialog" :title="qrParams.name">
|
||||
<n-form class="py-4">
|
||||
<div class="text-center">
|
||||
<qrcode-vue :value="qrParams.qrUrl" :size="220" class="canvas" style="margin: 0 auto" />
|
||||
</div>
|
||||
</n-form>
|
||||
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="() => (showQrModal = false)">关闭</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
<RechargeLog class="mt-6" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, inject } from 'vue';
|
||||
import wx from 'weixin-js-sdk';
|
||||
import { WechatOutlined, AlipayOutlined, QqOutlined, CheckOutlined } from '@vicons/antd';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { Create } from '@/api/order';
|
||||
import QrcodeVue from 'qrcode.vue';
|
||||
import RechargeLog from '../rechargeLog/index.vue';
|
||||
import { SocketEnum } from '@/enums/socketEnum';
|
||||
import { addOnMessage } from '@/utils/websocket';
|
||||
|
||||
const showQrModal = ref(false);
|
||||
const qrParams = ref({
|
||||
name: '',
|
||||
qrUrl: '',
|
||||
});
|
||||
const loading = ref(false);
|
||||
const message = useMessage();
|
||||
const amountType = ref(1);
|
||||
const amounts = ref([0.01, 10, 20, 30, 50, 100, '其他金额']);
|
||||
const payTypes = ref([
|
||||
{ value: 'wxpay', label: '微信支付', icon: WechatOutlined, color: '#18a058' },
|
||||
{ value: 'alipay', label: '支付宝', icon: AlipayOutlined, color: '#2d8cf0' },
|
||||
{ value: 'qqpay', label: 'QQ支付', icon: QqOutlined, color: '#2d8cf0' },
|
||||
]);
|
||||
|
||||
const amount = ref<any>(null);
|
||||
const payType = ref<any>(null);
|
||||
|
||||
onMounted(() => {});
|
||||
|
||||
function SetPayType(type: string) {
|
||||
payType.value = type;
|
||||
}
|
||||
|
||||
function SetAmount(a: number | string) {
|
||||
amount.value = a;
|
||||
if (a === '其他金额') {
|
||||
amountType.value = 2;
|
||||
amount.value = null;
|
||||
} else {
|
||||
amountType.value = 1;
|
||||
}
|
||||
}
|
||||
|
||||
function CreateOrder() {
|
||||
if (amount.value === null || amount.value <= 0) {
|
||||
message.error('请选择充值金额');
|
||||
return;
|
||||
}
|
||||
|
||||
if (payType.value === null || payType.value === '') {
|
||||
message.error('请选择支付方式');
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
Create({
|
||||
orderType: 'balance',
|
||||
payType: payType.value,
|
||||
money: amount.value,
|
||||
returnUrl: window.location.href,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.order?.tradeType === undefined || res.order?.tradeType === '') {
|
||||
message.error('创建支付订单失败,没找到交易方式,请联系管理处理!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.order?.tradeType !== 'mp') {
|
||||
if (res.order?.payURL === undefined || res.order?.payURL === '') {
|
||||
message.error('创建支付订单失败,没找到支付地址,请联系管理处理!');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (res.order?.tradeType) {
|
||||
case 'scan':
|
||||
showQr(res.order?.payURL, '打开微信【扫一扫】完成支付');
|
||||
break;
|
||||
case 'mp':
|
||||
if (res.order.jsApi === undefined) {
|
||||
message.error('支付失败请选择其他支付方式:JSAPI支付参数无效');
|
||||
return;
|
||||
}
|
||||
const jsApi = res.order.jsApi;
|
||||
|
||||
// 配置微信JS SDK
|
||||
wx.config({
|
||||
// debug: true,
|
||||
appId: jsApi.config.app_id,
|
||||
timestamp: jsApi.config.timestamp,
|
||||
nonceStr: jsApi.config.nonce_str,
|
||||
signature: jsApi.config.signature,
|
||||
jsApiList: ['chooseWXPay'],
|
||||
});
|
||||
// 配置完成后返回一个resolve
|
||||
wx.ready(() => {
|
||||
wxJSPay({
|
||||
timestamp: jsApi.params.timeStamp,
|
||||
nonceStr: jsApi.params.nonceStr,
|
||||
package: jsApi.params.package,
|
||||
signType: jsApi.params.signType,
|
||||
paySign: jsApi.params.paySign,
|
||||
})
|
||||
.then((_res) => {
|
||||
// ...
|
||||
})
|
||||
.catch((err) => {
|
||||
message.success('支付失败:', err.message);
|
||||
});
|
||||
});
|
||||
|
||||
break;
|
||||
case 'qqweb':
|
||||
showQr(res.order?.payURL, '打开QQ【扫一扫】完成支付');
|
||||
break;
|
||||
default:
|
||||
window.open(res.order?.payURL, '_blank');
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 发起微信公众号支付
|
||||
function wxJSPay(params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 调用微信支付
|
||||
wx.chooseWXPay({
|
||||
timestamp: params.timestamp,
|
||||
nonceStr: params.nonceStr,
|
||||
package: params.package,
|
||||
signType: params.signType,
|
||||
paySign: params.paySign,
|
||||
success: (res) => {
|
||||
// 支付成功时返回resolve
|
||||
resolve(res);
|
||||
},
|
||||
fail: (err) => {
|
||||
// 支付失败时返回reject
|
||||
reject(err);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showQr(url: string, name: string) {
|
||||
qrParams.value.qrUrl = url;
|
||||
qrParams.value.name = name;
|
||||
showQrModal.value = true;
|
||||
}
|
||||
|
||||
const onMessageList = inject('onMessageList');
|
||||
|
||||
const handleMessageList = (res) => {
|
||||
const data = JSON.parse(res.data);
|
||||
if (data.event === SocketEnum.EventAdminOrderNotify) {
|
||||
if (data.code == SocketEnum.CodeErr) {
|
||||
message.error('查询出错:' + data.event);
|
||||
return;
|
||||
}
|
||||
|
||||
showQrModal.value = false;
|
||||
message.success('支付成功');
|
||||
|
||||
location.reload();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
addOnMessage(onMessageList, handleMessageList);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
::v-deep(.n-thing .n-thing-main .n-thing-main__footer:not(:first-child)) {
|
||||
margin-top: 36px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
::v-deep(.title) {
|
||||
font-weight: var(--n-title-font-weight);
|
||||
transition: color 0.3s var(--n-bezier);
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
color: var(--n-title-text-color);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
::v-deep(.check-icon) {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
::v-deep(.create-order-button) {
|
||||
margin-top: 28px;
|
||||
}
|
||||
</style>
|
149
web/src/views/asset/rechargeLog/acceptRefund.vue
Normal file
149
web/src/views/asset/rechargeLog/acceptRefund.vue
Normal file
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-spin :show="loading" description="请稍候...">
|
||||
<n-modal
|
||||
v-model:show="isShowModal"
|
||||
:show-icon="false"
|
||||
preset="dialog"
|
||||
title="受理退款申请"
|
||||
:style="{
|
||||
width: dialogWidth,
|
||||
}"
|
||||
>
|
||||
<n-form
|
||||
:model="params"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
label-placement="left"
|
||||
:label-width="80"
|
||||
class="py-4"
|
||||
>
|
||||
<n-form-item label="业务单号" path="orderSn">
|
||||
<n-input v-model:value="params.orderSn" :disabled="true" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="订单金额" path="money">
|
||||
<n-input placeholder="请输入标题" v-model:value="params.money" :disabled="true" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="退款原因" path="refundReason">
|
||||
<n-input
|
||||
type="textarea"
|
||||
placeholder="请填写退款原因"
|
||||
v-model:value="params.refundReason"
|
||||
:disabled="true"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="更新状态" path="status">
|
||||
<n-select v-model:value="params.status" :options="options.acceptRefundStatus" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="拒绝原因" path="rejectRefundReason" v-if="params.status === 9">
|
||||
<n-input
|
||||
type="textarea"
|
||||
placeholder="请填拒绝退款原因"
|
||||
v-model:value="params.rejectRefundReason"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="退款备注" path="remark" v-if="params.status === 8">
|
||||
<n-input type="textarea" placeholder="请填退款备注" v-model:value="params.remark" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="closeForm">取消</n-button>
|
||||
<n-button type="info" :loading="formBtnLoading" @click="confirmForm">确定</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, computed, watch } from 'vue';
|
||||
import { rules, State, newState, options } from './model';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { adaModalWidth } from '@/utils/hotgo';
|
||||
import { AcceptRefund, View } from '@/api/order';
|
||||
const emit = defineEmits(['reloadTable', 'updateShowModal']);
|
||||
|
||||
interface Props {
|
||||
showModal: boolean;
|
||||
formParams?: State;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
showModal: false,
|
||||
formParams: () => {
|
||||
return newState(null);
|
||||
},
|
||||
});
|
||||
|
||||
const isShowModal = computed({
|
||||
get: () => {
|
||||
return props.showModal;
|
||||
},
|
||||
set: (value) => {
|
||||
emit('updateShowModal', value);
|
||||
},
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const params = ref<State>(props.formParams);
|
||||
const message = useMessage();
|
||||
const formRef = ref<any>({});
|
||||
const dialogWidth = ref('75%');
|
||||
const formBtnLoading = ref(false);
|
||||
|
||||
function confirmForm(e) {
|
||||
e.preventDefault();
|
||||
formBtnLoading.value = true;
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
AcceptRefund(params.value).then((_res) => {
|
||||
message.success('操作成功');
|
||||
setTimeout(() => {
|
||||
isShowModal.value = false;
|
||||
emit('reloadTable');
|
||||
});
|
||||
});
|
||||
} else {
|
||||
message.error('请填写完整信息');
|
||||
}
|
||||
formBtnLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
adaModalWidth(dialogWidth);
|
||||
});
|
||||
|
||||
function closeForm() {
|
||||
isShowModal.value = false;
|
||||
}
|
||||
|
||||
function loadForm(value) {
|
||||
loading.value = true;
|
||||
|
||||
// 编辑
|
||||
View({ id: value.id })
|
||||
.then((res) => {
|
||||
params.value = res;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.formParams,
|
||||
(value) => {
|
||||
loadForm(value);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less"></style>
|
132
web/src/views/asset/rechargeLog/applyRefund.vue
Normal file
132
web/src/views/asset/rechargeLog/applyRefund.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-spin :show="loading" description="请稍候...">
|
||||
<n-modal
|
||||
v-model:show="isShowModal"
|
||||
:show-icon="false"
|
||||
preset="dialog"
|
||||
title="退款申请"
|
||||
:style="{
|
||||
width: dialogWidth,
|
||||
}"
|
||||
>
|
||||
<n-form
|
||||
:model="params"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
label-placement="left"
|
||||
:label-width="80"
|
||||
class="py-4"
|
||||
>
|
||||
<n-form-item label="业务单号" path="orderSn">
|
||||
<n-input v-model:value="params.orderSn" :disabled="true" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="订单金额" path="money">
|
||||
<n-input placeholder="请输入标题" v-model:value="params.money" :disabled="true" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="退款原因" path="refundReason">
|
||||
<n-input
|
||||
type="textarea"
|
||||
placeholder="请填写退款原因"
|
||||
v-model:value="params.refundReason"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="closeForm">取消</n-button>
|
||||
<n-button type="info" :loading="formBtnLoading" @click="confirmForm">确定</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, computed, watch } from 'vue';
|
||||
import { rules, State, newState } from './model';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { adaModalWidth } from '@/utils/hotgo';
|
||||
import { ApplyRefund, View } from '@/api/order';
|
||||
const emit = defineEmits(['reloadTable', 'updateShowModal']);
|
||||
|
||||
interface Props {
|
||||
showModal: boolean;
|
||||
formParams?: State;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
showModal: false,
|
||||
formParams: () => {
|
||||
return newState(null);
|
||||
},
|
||||
});
|
||||
|
||||
const isShowModal = computed({
|
||||
get: () => {
|
||||
return props.showModal;
|
||||
},
|
||||
set: (value) => {
|
||||
emit('updateShowModal', value);
|
||||
},
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const params = ref<State>(props.formParams);
|
||||
const message = useMessage();
|
||||
const formRef = ref<any>({});
|
||||
const dialogWidth = ref('75%');
|
||||
const formBtnLoading = ref(false);
|
||||
|
||||
function confirmForm(e) {
|
||||
e.preventDefault();
|
||||
formBtnLoading.value = true;
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
ApplyRefund(params.value).then((_res) => {
|
||||
message.success('操作成功');
|
||||
setTimeout(() => {
|
||||
isShowModal.value = false;
|
||||
emit('reloadTable');
|
||||
});
|
||||
});
|
||||
} else {
|
||||
message.error('请填写完整信息');
|
||||
}
|
||||
formBtnLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
adaModalWidth(dialogWidth);
|
||||
});
|
||||
|
||||
function closeForm() {
|
||||
isShowModal.value = false;
|
||||
}
|
||||
|
||||
function loadForm(value) {
|
||||
loading.value = true;
|
||||
|
||||
// 编辑
|
||||
View({ id: value.id })
|
||||
.then((res) => {
|
||||
params.value = res;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.formParams,
|
||||
(value) => {
|
||||
loadForm(value);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less"></style>
|
45
web/src/views/asset/rechargeLog/index.vue
Normal file
45
web/src/views/asset/rechargeLog/index.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="充值记录" />
|
||||
</div>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<n-tabs
|
||||
type="card"
|
||||
class="card-tabs"
|
||||
:value="defaultTab"
|
||||
animated
|
||||
@before-leave="handleBeforeLeave"
|
||||
>
|
||||
<n-tab-pane
|
||||
:name="item.key.toString()"
|
||||
:tab="item.label"
|
||||
v-for="item in options.status"
|
||||
:key="item.key"
|
||||
>
|
||||
<List :type="defaultTab" />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import List from './list.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { options } from '@/views/asset/rechargeLog/model';
|
||||
|
||||
const router = useRouter();
|
||||
const defaultTab = ref('-1');
|
||||
|
||||
onMounted(() => {
|
||||
if (router.currentRoute.value.query?.type) {
|
||||
defaultTab.value = router.currentRoute.value.query.type as string;
|
||||
}
|
||||
});
|
||||
|
||||
function handleBeforeLeave(tabName: string) {
|
||||
defaultTab.value = tabName;
|
||||
}
|
||||
</script>
|
228
web/src/views/asset/rechargeLog/list.vue
Normal file
228
web/src/views/asset/rechargeLog/list.vue
Normal file
@@ -0,0 +1,228 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<BasicForm
|
||||
@register="register"
|
||||
@submit="reloadTable"
|
||||
@reset="reloadTable"
|
||||
@keyup.enter="reloadTable"
|
||||
ref="searchFormRef"
|
||||
>
|
||||
<template #statusSlot="{ model, field }">
|
||||
<n-input v-model:value="model[field]" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
|
||||
<BasicTable
|
||||
:openChecked="true"
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
:actionColumn="actionColumn"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
:scroll-x="1800"
|
||||
:resizeHeightOffset="-10000"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<n-button
|
||||
type="error"
|
||||
@click="handleBatchDelete"
|
||||
:disabled="batchDeleteDisabled"
|
||||
class="min-left-space"
|
||||
v-if="hasPermission(['/order/delete'])"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<DeleteOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
批量删除
|
||||
</n-button>
|
||||
|
||||
<n-button
|
||||
type="primary"
|
||||
@click="handleExport"
|
||||
class="min-left-space"
|
||||
v-if="hasPermission(['/order/export'])"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<ExportOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
导出
|
||||
</n-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</n-card>
|
||||
|
||||
<ApplyRefund
|
||||
@reloadTable="reloadTable"
|
||||
@updateShowModal="updateShowModal"
|
||||
:showModal="showModal"
|
||||
:formParams="formParams"
|
||||
/>
|
||||
|
||||
<AcceptRefund
|
||||
@reloadTable="reloadTable"
|
||||
@updateShowModal="updateAcceptShowModal"
|
||||
:showModal="showAcceptModal"
|
||||
:formParams="formParams"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { h, reactive, ref } from 'vue';
|
||||
import { useDialog, useMessage } from 'naive-ui';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { BasicForm, useForm } from '@/components/Form/index';
|
||||
import { usePermission } from '@/hooks/web/usePermission';
|
||||
import { List, Export, Delete } from '@/api/order';
|
||||
import { State, columns, schemas, newState } from './model';
|
||||
import { ExportOutlined, DeleteOutlined } from '@vicons/antd';
|
||||
import ApplyRefund from './applyRefund.vue';
|
||||
import AcceptRefund from './acceptRefund.vue';
|
||||
interface Props {
|
||||
type?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
type: '-1',
|
||||
});
|
||||
const { hasPermission } = usePermission();
|
||||
const actionRef = ref();
|
||||
const dialog = useDialog();
|
||||
const message = useMessage();
|
||||
const searchFormRef = ref<any>({});
|
||||
const batchDeleteDisabled = ref(true);
|
||||
const checkedIds = ref([]);
|
||||
const showModal = ref(false);
|
||||
const showAcceptModal = ref(false);
|
||||
const formParams = ref<State>();
|
||||
|
||||
const actionColumn = reactive({
|
||||
width: 120,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
render(record) {
|
||||
return h(TableAction as any, {
|
||||
style: 'button',
|
||||
actions: [
|
||||
{
|
||||
type: 'warning',
|
||||
label: '受理退款',
|
||||
onClick: handleAcceptRefund.bind(null, record),
|
||||
auth: ['/order/acceptRefund'],
|
||||
ifShow: () => {
|
||||
return record.status == 6;
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'default',
|
||||
label: '申请退款',
|
||||
onClick: handleApplyRefund.bind(null, record),
|
||||
auth: ['/order/applyRefund'],
|
||||
ifShow: () => {
|
||||
return record.status == 4;
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
onClick: handleDelete.bind(null, record),
|
||||
auth: ['/order/delete'],
|
||||
ifShow: () => {
|
||||
return record.status == 5;
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const [register, {}] = useForm({
|
||||
gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
|
||||
labelWidth: 80,
|
||||
schemas,
|
||||
});
|
||||
|
||||
const loadDataTable = async (res) => {
|
||||
return await List({ ...searchFormRef.value?.formModel, ...res, ...{ status: props.type } });
|
||||
};
|
||||
|
||||
function reloadTable() {
|
||||
actionRef.value.reload();
|
||||
}
|
||||
|
||||
function onCheckedRow(rowKeys) {
|
||||
batchDeleteDisabled.value = rowKeys.length <= 0;
|
||||
checkedIds.value = rowKeys;
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
message.loading('正在导出列表...', { duration: 1200 });
|
||||
Export(searchFormRef.value?.formModel);
|
||||
}
|
||||
|
||||
function handleDelete(record: Recordable) {
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
content: '你确定要删除?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
Delete(record).then((_res) => {
|
||||
message.success('删除成功');
|
||||
reloadTable();
|
||||
});
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
// message.error('取消');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleBatchDelete() {
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
content: '你确定要批量删除?只有已关闭的订单才能被删除',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
Delete({ id: checkedIds.value }).then((_res) => {
|
||||
message.success('删除成功');
|
||||
reloadTable();
|
||||
});
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
// message.error('取消');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function updateShowModal(value) {
|
||||
showModal.value = value;
|
||||
}
|
||||
|
||||
function handleApplyRefund(record: Recordable) {
|
||||
showModal.value = true;
|
||||
formParams.value = newState(record as State);
|
||||
}
|
||||
|
||||
function updateAcceptShowModal(value) {
|
||||
showAcceptModal.value = value;
|
||||
}
|
||||
|
||||
function handleAcceptRefund(record: Recordable) {
|
||||
showAcceptModal.value = true;
|
||||
formParams.value = newState(record as State);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
reloadTable,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
209
web/src/views/asset/rechargeLog/model.ts
Normal file
209
web/src/views/asset/rechargeLog/model.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
import { h, ref } from 'vue';
|
||||
import { NTag } from 'naive-ui';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { FormSchema } from '@/components/Form';
|
||||
import { Option } from '@/api/order';
|
||||
import { isNullObject } from '@/utils/is';
|
||||
import { defRangeShortcuts } from '@/utils/dateUtil';
|
||||
import { getOptionLabel, getOptionTag, Options } from '@/utils/hotgo';
|
||||
|
||||
export interface State {
|
||||
id: number;
|
||||
memberId: number;
|
||||
orderType: string;
|
||||
productId: number;
|
||||
orderSn: string;
|
||||
money: number;
|
||||
remark: string;
|
||||
payLogOutTradeNo: string;
|
||||
status: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
refundReason: string;
|
||||
rejectRefundReason: string;
|
||||
payLogPayType: string;
|
||||
}
|
||||
|
||||
export const defaultState = {
|
||||
id: 0,
|
||||
memberId: 0,
|
||||
orderType: '',
|
||||
productId: 0,
|
||||
orderSn: '',
|
||||
money: 0,
|
||||
payLogOutTradeNo: '',
|
||||
remark: '',
|
||||
status: 1,
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
refundReason: '',
|
||||
rejectRefundReason: '',
|
||||
payLogPayType: '',
|
||||
};
|
||||
|
||||
export function newState(state: State | null): State {
|
||||
if (state !== null) {
|
||||
return cloneDeep(state);
|
||||
}
|
||||
return cloneDeep(defaultState);
|
||||
}
|
||||
|
||||
export const options = ref<Options>({
|
||||
status: [],
|
||||
acceptRefundStatus: [],
|
||||
payType: [],
|
||||
});
|
||||
|
||||
export const rules = {};
|
||||
|
||||
export const schemas = ref<FormSchema[]>([
|
||||
{
|
||||
field: 'memberId',
|
||||
component: 'NInput',
|
||||
label: '管理员ID',
|
||||
componentProps: {
|
||||
placeholder: '请输入管理员ID',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'orderSn',
|
||||
component: 'NInput',
|
||||
label: '业务单号',
|
||||
componentProps: {
|
||||
placeholder: '请输入业务订单号',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'payLogOutTradeNo',
|
||||
component: 'NInput',
|
||||
label: '商户单号',
|
||||
componentProps: {
|
||||
placeholder: '请输入商户订单号',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'createdAt',
|
||||
component: 'NDatePicker',
|
||||
label: '创建时间',
|
||||
componentProps: {
|
||||
type: 'datetimerange',
|
||||
clearable: true,
|
||||
shortcuts: defRangeShortcuts(),
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: '订单ID',
|
||||
key: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '管理员ID',
|
||||
key: 'memberId',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '业务订单号',
|
||||
key: 'orderSn',
|
||||
width: 260,
|
||||
},
|
||||
{
|
||||
title: '商户订单号',
|
||||
key: 'payLogOutTradeNo',
|
||||
width: 260,
|
||||
},
|
||||
{
|
||||
title: '支付方式',
|
||||
key: 'payLogPayType',
|
||||
render(row) {
|
||||
if (isNullObject(row.payLogPayType)) {
|
||||
return ``;
|
||||
}
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
style: {
|
||||
marginRight: '6px',
|
||||
},
|
||||
type: getOptionTag(options.value.payType, row.payLogPayType),
|
||||
bordered: false,
|
||||
},
|
||||
{
|
||||
default: () => getOptionLabel(options.value.payType, row.payLogPayType),
|
||||
}
|
||||
);
|
||||
},
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '充值金额',
|
||||
key: 'money',
|
||||
width: 100,
|
||||
render(row) {
|
||||
return '¥' + Number(row.money).toFixed(2);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '订单状态',
|
||||
key: 'status',
|
||||
render(row) {
|
||||
if (isNullObject(row.status)) {
|
||||
return ``;
|
||||
}
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
style: {
|
||||
marginRight: '6px',
|
||||
},
|
||||
type: getOptionTag(options.value.status, row.status),
|
||||
bordered: false,
|
||||
},
|
||||
{
|
||||
default: () =>
|
||||
getOptionLabel(options.value.status, row.status) +
|
||||
(row.status === 9 ? ',' + row.rejectRefundReason : ''),
|
||||
}
|
||||
);
|
||||
},
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'createdAt',
|
||||
width: 180,
|
||||
},
|
||||
];
|
||||
|
||||
async function loadOptions() {
|
||||
options.value = await Option();
|
||||
for (const item of schemas.value) {
|
||||
switch (item.field) {
|
||||
case 'status':
|
||||
item.componentProps.options = options.value.status;
|
||||
break;
|
||||
case 'acceptRefundStatus':
|
||||
item.componentProps.options = options.value.acceptRefundStatus;
|
||||
break;
|
||||
case 'payType':
|
||||
item.componentProps.options = options.value.payType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await loadOptions();
|
@@ -1,162 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="拖拽"> 常用于卡片,事项,预约,流程,计划等, </n-card>
|
||||
</div>
|
||||
|
||||
<n-alert title="花式拖拽演示" type="info" class="mt-4">
|
||||
每个卡片,都可以上下拖拽顺序,另外不同卡片,也可以拖拽过去,拖拽过来,都不在话下呢,快试试O(∩_∩)O哈哈~
|
||||
</n-alert>
|
||||
|
||||
<n-grid
|
||||
cols="1 s:2 m:3 l:4 xl:4 2xl:4"
|
||||
class="mt-4 proCard"
|
||||
responsive="screen"
|
||||
:x-gap="12"
|
||||
:y-gap="8"
|
||||
>
|
||||
<n-grid-item>
|
||||
<NCard
|
||||
title="需求池"
|
||||
:segmented="{ content: true, footer: true }"
|
||||
size="small"
|
||||
:bordered="false"
|
||||
>
|
||||
<template #header-extra>
|
||||
<n-tag type="info">月</n-tag>
|
||||
</template>
|
||||
|
||||
<Draggable
|
||||
class="draggable-ul"
|
||||
animation="300"
|
||||
:list="demandList"
|
||||
group="people"
|
||||
itemKey="name"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="cursor-move draggable-li">
|
||||
<n-tag type="info">需求</n-tag><span class="ml-2">{{ element.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</Draggable>
|
||||
</NCard>
|
||||
</n-grid-item>
|
||||
|
||||
<n-grid-item>
|
||||
<NCard
|
||||
title="开发中"
|
||||
:segmented="{ content: true, footer: true }"
|
||||
size="small"
|
||||
:bordered="false"
|
||||
>
|
||||
<template #header-extra>
|
||||
<n-tag type="info">月</n-tag>
|
||||
</template>
|
||||
|
||||
<Draggable
|
||||
class="draggable-ul"
|
||||
animation="300"
|
||||
:list="exploitList"
|
||||
group="people"
|
||||
itemKey="name"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="cursor-move draggable-li">
|
||||
<n-tag type="warning">开发中</n-tag><span class="ml-2">{{ element.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</Draggable>
|
||||
</NCard>
|
||||
</n-grid-item>
|
||||
|
||||
<n-grid-item>
|
||||
<NCard
|
||||
title="已完成"
|
||||
:segmented="{ content: true, footer: true }"
|
||||
size="small"
|
||||
:bordered="false"
|
||||
>
|
||||
<template #header-extra>
|
||||
<n-tag type="info">月</n-tag>
|
||||
</template>
|
||||
<Draggable
|
||||
class="draggable-ul"
|
||||
animation="300"
|
||||
:list="completeList"
|
||||
group="people"
|
||||
itemKey="name"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="cursor-move draggable-li">
|
||||
<n-tag type="error">已完成</n-tag><span class="ml-2">{{ element.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</Draggable>
|
||||
</NCard>
|
||||
</n-grid-item>
|
||||
|
||||
<n-grid-item>
|
||||
<NCard
|
||||
title="已验收"
|
||||
:segmented="{ content: true, footer: true }"
|
||||
size="small"
|
||||
:bordered="false"
|
||||
>
|
||||
<template #header-extra>
|
||||
<n-tag type="info">月</n-tag>
|
||||
</template>
|
||||
<Draggable
|
||||
class="draggable-ul"
|
||||
animation="300"
|
||||
:list="approvedList"
|
||||
group="people"
|
||||
itemKey="name"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="cursor-move draggable-li">
|
||||
<n-tag type="success">已验收</n-tag><span class="ml-2">{{ element.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</Draggable>
|
||||
</NCard>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue';
|
||||
import Draggable from 'vuedraggable';
|
||||
|
||||
const demandList = reactive([
|
||||
{ name: '预约表单页面,能填写预约相关信息', id: 1 },
|
||||
{ name: '促销活动页面,包含促销广告展示', id: 2 },
|
||||
{ name: '商品列表,需要一个到货提醒功能', id: 3 },
|
||||
{ name: '商品需要一个评价功能', id: 4 },
|
||||
{ name: '商品图片需要提供放大镜', id: 5 },
|
||||
{ name: '订单需要提供删除到回收站', id: 6 },
|
||||
{ name: '用户头像上传,需要支持裁剪', id: 7 },
|
||||
{ name: '据说Vue3.2发布了,setup啥时候支持?', id: 8 },
|
||||
]);
|
||||
|
||||
const exploitList = reactive([{ name: '商品图片需要提供放大镜', id: 5 }]);
|
||||
|
||||
const completeList = reactive([{ name: '商品图片需要提供放大镜', id: 5 }]);
|
||||
|
||||
const approvedList = reactive([{ name: '商品图片需要提供放大镜', id: 5 }]);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.draggable-ul {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
margin-top: -16px;
|
||||
|
||||
.draggable-li {
|
||||
width: 100%;
|
||||
padding: 16px 10px;
|
||||
color: #333;
|
||||
border-bottom: 1px solid #efeff5;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -1,172 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="基础表单"> 基础表单,用于向用户收集表单信息 </n-card>
|
||||
</div>
|
||||
<n-card :bordered="false" class="mt-4 proCard">
|
||||
<div class="BasicForm">
|
||||
<BasicForm
|
||||
submitButtonText="提交预约"
|
||||
layout="horizontal"
|
||||
:gridProps="{ cols: 1 }"
|
||||
:schemas="schemas"
|
||||
@submit="handleSubmit"
|
||||
@reset="handleReset"
|
||||
>
|
||||
<template #statusSlot="{ model, field }">
|
||||
<n-input v-model:value="model[field]" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
</div>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { BasicForm } from '@/components/Form/index';
|
||||
import { useMessage } from 'naive-ui';
|
||||
|
||||
const schemas = [
|
||||
{
|
||||
field: 'name',
|
||||
component: 'NInput',
|
||||
label: '姓名',
|
||||
labelMessage: '这是一个提示',
|
||||
componentProps: {
|
||||
placeholder: '请输入姓名',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
rules: [{ required: true, message: '请输入姓名', trigger: ['blur'] }],
|
||||
},
|
||||
{
|
||||
field: 'mobile',
|
||||
component: 'NInputNumber',
|
||||
label: '手机',
|
||||
componentProps: {
|
||||
placeholder: '请输入手机号码',
|
||||
showButton: false,
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
component: 'NSelect',
|
||||
label: '类型',
|
||||
componentProps: {
|
||||
placeholder: '请选择类型',
|
||||
options: [
|
||||
{
|
||||
label: '舒适性',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '经济性',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeDate',
|
||||
component: 'NDatePicker',
|
||||
label: '预约时间',
|
||||
defaultValue: 1183135260000,
|
||||
componentProps: {
|
||||
type: 'date',
|
||||
clearable: true,
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeTime',
|
||||
component: 'NTimePicker',
|
||||
label: '停留时间',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeProject',
|
||||
component: 'NCheckbox',
|
||||
label: '预约项目',
|
||||
componentProps: {
|
||||
placeholder: '请选择预约项目',
|
||||
options: [
|
||||
{
|
||||
label: '种牙',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '补牙',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '根管',
|
||||
value: 3,
|
||||
},
|
||||
],
|
||||
onUpdateChecked: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeSource',
|
||||
component: 'NRadioGroup',
|
||||
label: '来源',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '网上',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '门店',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
onUpdateChecked: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '状态',
|
||||
//插槽
|
||||
slot: 'statusSlot',
|
||||
},
|
||||
];
|
||||
|
||||
const message = useMessage();
|
||||
|
||||
function handleSubmit(values: Recordable) {
|
||||
console.log(values);
|
||||
message.success(JSON.stringify(values));
|
||||
}
|
||||
|
||||
function handleReset(values: Recordable) {
|
||||
console.log(values);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.BasicForm {
|
||||
width: 550px;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
padding-top: 20px;
|
||||
}
|
||||
</style>
|
@@ -1,195 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="基础表单"> useForm 表单,用于向用户收集表单信息 </n-card>
|
||||
</div>
|
||||
<n-card :bordered="false" class="mt-4 proCard">
|
||||
<div class="BasicForm">
|
||||
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset">
|
||||
<template #statusSlot="{ model, field }">
|
||||
<n-input v-model:value="model[field]" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
</div>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { BasicForm, FormSchema, useForm } from '@/components/Form/index';
|
||||
import { useMessage } from 'naive-ui';
|
||||
|
||||
const schemas: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
component: 'NInput',
|
||||
label: '姓名',
|
||||
labelMessage: '这是一个提示',
|
||||
giProps: {
|
||||
span: 1,
|
||||
},
|
||||
componentProps: {
|
||||
placeholder: '请输入姓名',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
rules: [{ required: true, message: '请输入姓名', trigger: ['blur'] }],
|
||||
},
|
||||
{
|
||||
field: 'mobile',
|
||||
component: 'NInputNumber',
|
||||
label: '手机',
|
||||
componentProps: {
|
||||
placeholder: '请输入手机号码',
|
||||
showButton: false,
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
component: 'NSelect',
|
||||
label: '类型',
|
||||
giProps: {
|
||||
//span: 24,
|
||||
},
|
||||
componentProps: {
|
||||
placeholder: '请选择类型',
|
||||
options: [
|
||||
{
|
||||
label: '舒适性',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '经济性',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeDate',
|
||||
component: 'NDatePicker',
|
||||
label: '预约时间',
|
||||
giProps: {
|
||||
//span: 24,
|
||||
},
|
||||
defaultValue: 1183135260000,
|
||||
componentProps: {
|
||||
type: 'date',
|
||||
clearable: true,
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeTime',
|
||||
component: 'NTimePicker',
|
||||
label: '停留时间',
|
||||
giProps: {
|
||||
//span: 24,
|
||||
},
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeProject',
|
||||
component: 'NCheckbox',
|
||||
label: '预约项目',
|
||||
giProps: {
|
||||
//span: 24,
|
||||
},
|
||||
componentProps: {
|
||||
placeholder: '请选择预约项目',
|
||||
options: [
|
||||
{
|
||||
label: '种牙',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '补牙',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '根管',
|
||||
value: 3,
|
||||
},
|
||||
],
|
||||
onUpdateChecked: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeSource',
|
||||
component: 'NRadioGroup',
|
||||
label: '来源',
|
||||
giProps: {
|
||||
//span: 24,
|
||||
},
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '网上',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '门店',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
onUpdateChecked: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '状态',
|
||||
giProps: {
|
||||
//span: 24,
|
||||
},
|
||||
//插槽
|
||||
slot: 'statusSlot',
|
||||
},
|
||||
];
|
||||
|
||||
const message = useMessage();
|
||||
|
||||
const [register, {}] = useForm({
|
||||
gridProps: { cols: 1 },
|
||||
collapsedRows: 3,
|
||||
labelWidth: 120,
|
||||
layout: 'horizontal',
|
||||
submitButtonText: '提交预约',
|
||||
schemas,
|
||||
});
|
||||
|
||||
function handleSubmit(values: Recordable) {
|
||||
console.log(values);
|
||||
message.success(JSON.stringify(values));
|
||||
}
|
||||
|
||||
function handleReset(values: Recordable) {
|
||||
console.log(values);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.BasicForm {
|
||||
width: 550px;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
padding-top: 20px;
|
||||
}
|
||||
</style>
|
@@ -1,306 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="模态框">
|
||||
模态框,用于向用户收集或展示信息,Modal 采用 Dialog 预设,扩展拖拽效果
|
||||
<br />
|
||||
以下是 useModal
|
||||
方式,ref方式,也支持,使用方式和其他组件一致,如:modalRef.value.closeModal()
|
||||
</n-card>
|
||||
</div>
|
||||
<n-card :bordered="false" class="proCard mt-4">
|
||||
<n-alert title="Modal嵌套Form" type="info">
|
||||
使用 useModal 进行弹窗展示和操作,并演示了在Modal内和Form组件,组合使用方法
|
||||
</n-alert>
|
||||
<n-divider />
|
||||
<n-space>
|
||||
<n-button type="primary" @click="showModal">打开Modal嵌套Form例子</n-button>
|
||||
</n-space>
|
||||
<n-divider />
|
||||
<n-alert title="个性化轻量级" type="info">
|
||||
使用 useModal 进行弹窗展示和操作,自定义配置,实现轻量级效果,更多配置,请参考文档
|
||||
</n-alert>
|
||||
<n-divider />
|
||||
<n-space>
|
||||
<n-button type="primary" @click="showLightModal">轻量级确认</n-button>
|
||||
</n-space>
|
||||
<n-divider />
|
||||
<n-alert title="提示" type="info">
|
||||
组件暴露了,setProps 方法,用于修改组件内部
|
||||
Props,比如标题,等,具体参考UI框架文档,DialogReactive Properties
|
||||
</n-alert>
|
||||
</n-card>
|
||||
|
||||
<basicModal @register="modalRegister" ref="modalRef" class="basicModal" @on-ok="okModal">
|
||||
<template #default>
|
||||
<BasicForm @register="register" @reset="handleReset" class="basicForm">
|
||||
<template #statusSlot="{ model, field }">
|
||||
<n-input v-model:value="model[field]" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
</template>
|
||||
</basicModal>
|
||||
|
||||
<basicModal
|
||||
@register="lightModalRegister"
|
||||
class="basicModalLight"
|
||||
ref="modalRef"
|
||||
@on-ok="lightOkModal"
|
||||
>
|
||||
<template #default>
|
||||
<p class="text-gray-500" style="padding-left: 35px">一些对话框内容</p>
|
||||
</template>
|
||||
</basicModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, reactive, toRefs } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { basicModal, useModal } from '@/components/Modal';
|
||||
import { BasicForm, FormSchema, useForm } from '@/components/Form/index';
|
||||
|
||||
const schemas: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
component: 'NInput',
|
||||
label: '姓名',
|
||||
labelMessage: '这是一个提示',
|
||||
giProps: {
|
||||
span: 1,
|
||||
},
|
||||
componentProps: {
|
||||
placeholder: '请输入姓名',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
rules: [{ required: true, message: '请输入姓名', trigger: ['blur'] }],
|
||||
},
|
||||
{
|
||||
field: 'mobile',
|
||||
component: 'NInputNumber',
|
||||
label: '手机',
|
||||
componentProps: {
|
||||
placeholder: '请输入手机号码',
|
||||
showButton: false,
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
component: 'NSelect',
|
||||
label: '类型',
|
||||
giProps: {
|
||||
//span: 24,
|
||||
},
|
||||
componentProps: {
|
||||
placeholder: '请选择类型',
|
||||
options: [
|
||||
{
|
||||
label: '舒适性',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '经济性',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeDate',
|
||||
component: 'NDatePicker',
|
||||
label: '预约时间',
|
||||
giProps: {
|
||||
//span: 24,
|
||||
},
|
||||
defaultValue: 1183135260000,
|
||||
componentProps: {
|
||||
type: 'date',
|
||||
clearable: true,
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeTime',
|
||||
component: 'NTimePicker',
|
||||
label: '停留时间',
|
||||
giProps: {
|
||||
//span: 24,
|
||||
},
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeProject',
|
||||
component: 'NCheckbox',
|
||||
label: '预约项目',
|
||||
giProps: {
|
||||
//span: 24,
|
||||
},
|
||||
componentProps: {
|
||||
placeholder: '请选择预约项目',
|
||||
options: [
|
||||
{
|
||||
label: '种牙',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '补牙',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '根管',
|
||||
value: 3,
|
||||
},
|
||||
],
|
||||
onUpdateChecked: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeSource',
|
||||
component: 'NRadioGroup',
|
||||
label: '来源',
|
||||
giProps: {
|
||||
//span: 24,
|
||||
},
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '网上',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '门店',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
onUpdateChecked: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '状态',
|
||||
giProps: {
|
||||
//span: 24,
|
||||
},
|
||||
//插槽
|
||||
slot: 'statusSlot',
|
||||
},
|
||||
];
|
||||
|
||||
export default defineComponent({
|
||||
components: { basicModal, BasicForm },
|
||||
setup() {
|
||||
const modalRef: any = ref(null);
|
||||
const message = useMessage();
|
||||
|
||||
const [modalRegister, { openModal, closeModal, setSubLoading }] = useModal({
|
||||
title: '新增预约',
|
||||
});
|
||||
|
||||
const [
|
||||
lightModalRegister,
|
||||
{
|
||||
openModal: lightOpenModal,
|
||||
closeModal: lightCloseModal,
|
||||
setSubLoading: lightSetSubLoading,
|
||||
},
|
||||
] = useModal({
|
||||
title: '确认对话框',
|
||||
showIcon: true,
|
||||
type: 'warning',
|
||||
closable: false,
|
||||
maskClosable: true,
|
||||
});
|
||||
|
||||
const [register, { submit }] = useForm({
|
||||
gridProps: { cols: 1 },
|
||||
collapsedRows: 3,
|
||||
labelWidth: 120,
|
||||
layout: 'horizontal',
|
||||
submitButtonText: '提交预约',
|
||||
showActionButtonGroup: false,
|
||||
schemas,
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
formValue: {
|
||||
name: '小马哥',
|
||||
},
|
||||
});
|
||||
|
||||
async function okModal() {
|
||||
const formRes = await submit();
|
||||
if (formRes) {
|
||||
closeModal();
|
||||
message.success('提交成功');
|
||||
} else {
|
||||
message.error('验证失败,请填写完整信息');
|
||||
setSubLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
function lightOkModal() {
|
||||
lightCloseModal();
|
||||
lightSetSubLoading(false);
|
||||
}
|
||||
|
||||
function showLightModal() {
|
||||
lightOpenModal();
|
||||
}
|
||||
|
||||
function showModal() {
|
||||
openModal();
|
||||
}
|
||||
|
||||
function handleReset(values: Recordable) {
|
||||
console.log(values);
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
modalRef,
|
||||
register,
|
||||
modalRegister,
|
||||
lightModalRegister,
|
||||
handleReset,
|
||||
showModal,
|
||||
okModal,
|
||||
lightOkModal,
|
||||
showLightModal,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.basicForm {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.n-dialog.basicModal {
|
||||
width: 640px;
|
||||
}
|
||||
|
||||
.n-dialog.basicModalLight {
|
||||
width: 416px;
|
||||
padding-top: 26px;
|
||||
}
|
||||
</style>
|
@@ -1,114 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="富文本">
|
||||
富文本,用于展示图文信息,比如商品详情,文章详情等...
|
||||
</n-card>
|
||||
</div>
|
||||
<n-card :bordered="false" class="mt-4 proCard">
|
||||
<QuillEditor
|
||||
ref="quillEditor"
|
||||
:options="options"
|
||||
v-model:content="myContent"
|
||||
style="height: 350px"
|
||||
@ready="readyQuill"
|
||||
class="quillEditor"
|
||||
/>
|
||||
<template #footer>
|
||||
<n-space>
|
||||
<n-button @click="addText">增加文本</n-button>
|
||||
<n-button @click="addImg">增加图片</n-button>
|
||||
<n-button @click="getHtml">获取HTML</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-card>
|
||||
<n-card :bordered="false" class="mt-4 proCard" title="HTML 内容">
|
||||
<n-input
|
||||
v-model:value="myContentHtml"
|
||||
type="textarea"
|
||||
placeholder="html"
|
||||
:autosize="{
|
||||
minRows: 3,
|
||||
maxRows: 6,
|
||||
}"
|
||||
/>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { QuillEditor } from '@vueup/vue-quill';
|
||||
import '@vueup/vue-quill/dist/vue-quill.snow.css';
|
||||
const quillEditor = ref();
|
||||
const myContent = ref(
|
||||
'<h4>Naive Ui Admin 是一个基于 vue3,vite2,TypeScript 的中后台解决方案</h4>'
|
||||
);
|
||||
const myContentHtml = ref(
|
||||
'<h4>Naive Ui Admin 是一个基于 vue3,vite2,TypeScript 的中后台解决方案</h4>'
|
||||
);
|
||||
|
||||
const options = reactive({
|
||||
modules: {
|
||||
toolbar: [
|
||||
['bold', 'italic', 'underline', 'strike'], // toggled buttons
|
||||
['blockquote', 'code-block'],
|
||||
|
||||
[{ header: 1 }, { header: 2 }], // custom button values
|
||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
||||
[{ script: 'sub' }, { script: 'super' }], // superscript/subscript
|
||||
[{ indent: '-1' }, { indent: '+1' }], // outdent/indent
|
||||
[{ direction: 'rtl' }], // text direction
|
||||
|
||||
[{ size: ['small', false, 'large', 'huge'] }], // custom dropdown
|
||||
[{ header: [1, 2, 3, 4, 5, 6, false] }],
|
||||
|
||||
[{ color: [] }, { background: [] }], // dropdown with defaults from theme
|
||||
[{ font: [] }],
|
||||
[{ align: [] }],
|
||||
['clean'],
|
||||
['image'],
|
||||
],
|
||||
},
|
||||
theme: 'snow',
|
||||
placeholder: '输入您喜欢的内容吧!',
|
||||
});
|
||||
|
||||
function readyQuill() {
|
||||
console.log('Quill准备好了');
|
||||
}
|
||||
|
||||
function getHtml() {
|
||||
myContentHtml.value = getHtmlVal();
|
||||
}
|
||||
|
||||
function addText() {
|
||||
const html = getHtmlVal() + '新增加的内容';
|
||||
quillEditor.value.setHTML(html);
|
||||
}
|
||||
|
||||
function addImg() {
|
||||
const html =
|
||||
getHtmlVal() +
|
||||
'<img style="width:100px" src="https://www.baidu.com/img/flexible/logo/pc/result.png"/>';
|
||||
quillEditor.value.setHTML(html);
|
||||
}
|
||||
|
||||
function getHtmlVal() {
|
||||
return quillEditor.value.getHTML();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.ql-toolbar.ql-snow {
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid #eee;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.ql-container.ql-snow {
|
||||
border: none;
|
||||
}
|
||||
</style>
|
@@ -1,83 +0,0 @@
|
||||
import { h } from 'vue';
|
||||
import { NAvatar } from 'naive-ui';
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: 'id',
|
||||
key: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '编码',
|
||||
key: 'no',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
key: 'name',
|
||||
editComponent: 'NInput',
|
||||
// 默认必填校验
|
||||
editRule: true,
|
||||
edit: true,
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '头像',
|
||||
key: 'avatar',
|
||||
width: 100,
|
||||
render(row) {
|
||||
return h(NAvatar, {
|
||||
size: 48,
|
||||
src: row.avatar,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '地址',
|
||||
key: 'address',
|
||||
editComponent: 'NSelect',
|
||||
editComponentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '广东省',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '浙江省',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
edit: true,
|
||||
width: 200,
|
||||
ellipsis: false,
|
||||
},
|
||||
{
|
||||
title: '开始日期',
|
||||
key: 'beginTime',
|
||||
edit: true,
|
||||
width: 160,
|
||||
editComponent: 'NDatePicker',
|
||||
editComponentProps: {
|
||||
type: 'datetime',
|
||||
format: 'yyyy-MM-dd HH:mm:ss',
|
||||
valueFormat: 'yyyy-MM-dd HH:mm:ss',
|
||||
},
|
||||
ellipsis: false,
|
||||
},
|
||||
{
|
||||
title: '结束日期',
|
||||
key: 'endTime',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'date',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '停留时间',
|
||||
key: 'time',
|
||||
width: 80,
|
||||
},
|
||||
];
|
@@ -1,113 +0,0 @@
|
||||
<template>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<BasicTable
|
||||
title="表格列表"
|
||||
titleTooltip="这是一个提示"
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
:actionColumn="actionColumn"
|
||||
:scroll-x="1360"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
>
|
||||
<template #toolbar>
|
||||
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, h } from 'vue';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { getTableList } from '@/api/table/list';
|
||||
import { columns } from './basicColumns';
|
||||
import { useDialog, useMessage } from 'naive-ui';
|
||||
import { DeleteOutlined, EditOutlined } from '@vicons/antd';
|
||||
|
||||
const message = useMessage();
|
||||
const dialog = useDialog();
|
||||
const actionRef = ref();
|
||||
|
||||
const params = reactive({
|
||||
pageSize: 5,
|
||||
name: 'xiaoMa',
|
||||
});
|
||||
|
||||
const actionColumn = reactive({
|
||||
width: 150,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
render(record) {
|
||||
return h(TableAction as any, {
|
||||
style: 'text',
|
||||
actions: createActions(record),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function createActions(record) {
|
||||
return [
|
||||
{
|
||||
label: '删除',
|
||||
type: 'error',
|
||||
// 配置 color 会覆盖 type
|
||||
color: 'red',
|
||||
icon: DeleteOutlined,
|
||||
onClick: handleDelete.bind(null, record),
|
||||
// 根据业务控制是否显示 isShow 和 auth 是并且关系
|
||||
ifShow: () => {
|
||||
return true;
|
||||
},
|
||||
// 根据权限控制是否显示: 有权限,会显示,支持多个
|
||||
auth: ['basic_list'],
|
||||
},
|
||||
{
|
||||
label: '编辑',
|
||||
type: 'primary',
|
||||
icon: EditOutlined,
|
||||
onClick: handleEdit.bind(null, record),
|
||||
ifShow: () => {
|
||||
return true;
|
||||
},
|
||||
auth: ['basic_list'],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const loadDataTable = async (res) => {
|
||||
return await getTableList({ ...params, ...res });
|
||||
};
|
||||
|
||||
function onCheckedRow(rowKeys) {
|
||||
console.log(rowKeys);
|
||||
}
|
||||
|
||||
function reloadTable() {
|
||||
actionRef.value.reload();
|
||||
}
|
||||
|
||||
function handleDelete(record) {
|
||||
console.log(record);
|
||||
dialog.info({
|
||||
title: '提示',
|
||||
content: `您想删除${record.name}`,
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
message.success('删除成功');
|
||||
},
|
||||
onNegativeClick: () => {},
|
||||
});
|
||||
}
|
||||
|
||||
function handleEdit(record) {
|
||||
console.log(record);
|
||||
message.success('您点击了编辑按钮');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
@@ -1,72 +0,0 @@
|
||||
import { h } from 'vue';
|
||||
import { NAvatar, NTag } from 'naive-ui';
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: 'id',
|
||||
key: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '编码',
|
||||
key: 'no',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
key: 'name',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '头像',
|
||||
key: 'avatar',
|
||||
width: 100,
|
||||
render(row) {
|
||||
return h(NAvatar, {
|
||||
size: 48,
|
||||
src: row.avatar,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '地址',
|
||||
key: 'address',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '开始日期',
|
||||
key: 'beginTime',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '结束日期',
|
||||
key: 'endTime',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
render(row) {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
type: row.status ? 'success' : 'error',
|
||||
},
|
||||
{
|
||||
default: () => (row.status ? '启用' : '禁用'),
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'date',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '停留时间',
|
||||
key: 'time',
|
||||
width: 80,
|
||||
},
|
||||
];
|
@@ -1,59 +0,0 @@
|
||||
<template>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<BasicTable
|
||||
title="表格列表"
|
||||
titleTooltip="这是一个提示"
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
@edit-end="editEnd"
|
||||
@edit-change="onEditChange"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
:scroll-x="1360"
|
||||
>
|
||||
<template #toolbar>
|
||||
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { BasicTable } from '@/components/Table';
|
||||
import { getTableList } from '@/api/table/list';
|
||||
import { columns } from './CellColumns';
|
||||
|
||||
const actionRef = ref();
|
||||
const params = reactive({
|
||||
pageSize: 5,
|
||||
name: 'xiaoMa',
|
||||
});
|
||||
|
||||
function onEditChange({ column, value, record }) {
|
||||
if (column.key === 'id') {
|
||||
record.editValueRefs.name4.value = `${value}`;
|
||||
}
|
||||
console.log(column, value, record);
|
||||
}
|
||||
|
||||
const loadDataTable = async (res) => {
|
||||
return await getTableList({ ...params, ...res });
|
||||
};
|
||||
|
||||
function onCheckedRow(rowKeys) {
|
||||
console.log(rowKeys);
|
||||
}
|
||||
|
||||
function reloadTable() {
|
||||
console.log(actionRef.value);
|
||||
actionRef.value.reload();
|
||||
}
|
||||
|
||||
function editEnd({ record, index, key, value }) {
|
||||
console.log(value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
@@ -1,114 +0,0 @@
|
||||
<template>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<BasicTable
|
||||
title="表格列表"
|
||||
titleTooltip="这是一个提示"
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
:actionColumn="actionColumn"
|
||||
@edit-end="editEnd"
|
||||
@edit-change="onEditChange"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
:scroll-x="1590"
|
||||
>
|
||||
<template #toolbar>
|
||||
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, h } from 'vue';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { getTableList } from '@/api/table/list';
|
||||
import { columns } from './rowColumns';
|
||||
|
||||
const actionRef = ref();
|
||||
const currentEditKeyRef = ref('');
|
||||
const params = reactive({
|
||||
pageSize: 5,
|
||||
name: 'xiaoMa',
|
||||
});
|
||||
|
||||
const actionColumn = reactive({
|
||||
width: 150,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
render(record) {
|
||||
return h(TableAction, {
|
||||
style: 'button',
|
||||
actions: createActions(record),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function handleEdit(record) {
|
||||
currentEditKeyRef.value = record.key;
|
||||
record.onEdit?.(true);
|
||||
}
|
||||
|
||||
function handleCancel(record) {
|
||||
currentEditKeyRef.value = '';
|
||||
record.onEdit?.(false, false);
|
||||
}
|
||||
|
||||
function onEditChange({ column, value, record }) {
|
||||
if (column.key === 'id') {
|
||||
record.editValueRefs.name4.value = `${value}`;
|
||||
}
|
||||
console.log(column, value, record);
|
||||
}
|
||||
|
||||
async function handleSave(record) {
|
||||
const pass = await record.onEdit?.(false, true);
|
||||
if (pass) {
|
||||
currentEditKeyRef.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
function createActions(record) {
|
||||
if (!record.editable) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
{
|
||||
label: '保存',
|
||||
onClick: handleSave.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '取消',
|
||||
onClick: handleCancel.bind(null, record),
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
const loadDataTable = async (res) => {
|
||||
return await getTableList({ ...params, ...res });
|
||||
};
|
||||
|
||||
function onCheckedRow(rowKeys) {
|
||||
console.log(rowKeys);
|
||||
}
|
||||
|
||||
function reloadTable() {
|
||||
console.log(actionRef.value);
|
||||
actionRef.value.reload();
|
||||
}
|
||||
|
||||
function editEnd({ record, index, key, value }) {
|
||||
console.log(value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
@@ -1,97 +0,0 @@
|
||||
import { h } from 'vue';
|
||||
import { NAvatar } from 'naive-ui';
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: 'id',
|
||||
key: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '编码',
|
||||
key: 'no',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
key: 'name',
|
||||
editComponent: 'NInput',
|
||||
editRow: true,
|
||||
// 默认必填校验
|
||||
editRule: true,
|
||||
edit: true,
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '头像',
|
||||
key: 'avatar',
|
||||
width: 100,
|
||||
render(row) {
|
||||
return h(NAvatar, {
|
||||
size: 48,
|
||||
src: row.avatar,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '地址',
|
||||
key: 'address',
|
||||
editRow: true,
|
||||
editComponent: 'NSelect',
|
||||
editComponentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '广东省',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '浙江省',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
edit: true,
|
||||
width: 200,
|
||||
ellipsis: false,
|
||||
},
|
||||
{
|
||||
title: '开始日期',
|
||||
key: 'beginTime',
|
||||
editRow: true,
|
||||
edit: true,
|
||||
width: 240,
|
||||
editComponent: 'NDatePicker',
|
||||
editComponentProps: {
|
||||
type: 'datetime',
|
||||
format: 'yyyy-MM-dd HH:mm:ss',
|
||||
valueFormat: 'yyyy-MM-dd HH:mm:ss',
|
||||
},
|
||||
ellipsis: false,
|
||||
},
|
||||
{
|
||||
title: '结束日期',
|
||||
key: 'endTime',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
editRow: true,
|
||||
edit: true,
|
||||
width: 100,
|
||||
editComponent: 'NSwitch',
|
||||
editValueMap: (value) => {
|
||||
return value ? '启用' : '禁用';
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'date',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '停留时间',
|
||||
key: 'time',
|
||||
width: 80,
|
||||
},
|
||||
];
|
@@ -1,111 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="上传图片"> 上传图片,用于向用户收集图片信息 </n-card>
|
||||
</div>
|
||||
<n-card :bordered="false" class="mt-4 proCard">
|
||||
<n-grid cols="2 s:1 m:3 l:3 xl:3 2xl:3" responsive="screen">
|
||||
<n-grid-item offset="0 s:0 m:1 l:1 xl:1 2xl:1">
|
||||
<n-form
|
||||
:label-width="80"
|
||||
:model="formValue"
|
||||
:rules="rules"
|
||||
label-placement="left"
|
||||
ref="formRef"
|
||||
class="py-8"
|
||||
>
|
||||
<n-form-item label="预约姓名" path="name">
|
||||
<n-input v-model:value="formValue.name" placeholder="输入姓名" />
|
||||
</n-form-item>
|
||||
<n-form-item label="预约号码" path="mobile">
|
||||
<n-input placeholder="电话号码" v-model:value="formValue.mobile" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="病例图片" path="images">
|
||||
<BasicUpload
|
||||
:action="`${uploadUrl}/v1.0/files`"
|
||||
:headers="uploadHeaders"
|
||||
:data="{ type: 0 }"
|
||||
name="files"
|
||||
:width="100"
|
||||
:height="100"
|
||||
@uploadChange="uploadChange"
|
||||
v-model:value="formValue.images"
|
||||
helpText="单个文件不超过2MB,最多只能上传10个文件"
|
||||
/>
|
||||
</n-form-item>
|
||||
<div style="margin-left: 80px">
|
||||
<n-space>
|
||||
<n-button type="primary" @click="formSubmit">提交预约</n-button>
|
||||
<n-button @click="resetForm">重置</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref, reactive } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { BasicUpload } from '@/components/Upload';
|
||||
import { useGlobSetting } from '@/hooks/setting';
|
||||
|
||||
const globSetting = useGlobSetting();
|
||||
|
||||
const rules = {
|
||||
name: {
|
||||
required: true,
|
||||
message: '请输入预约姓名',
|
||||
trigger: 'blur',
|
||||
},
|
||||
remark: {
|
||||
required: true,
|
||||
message: '请输入预约备注',
|
||||
trigger: 'blur',
|
||||
},
|
||||
images: {
|
||||
required: true,
|
||||
type: 'array',
|
||||
message: '请上传病例图片',
|
||||
trigger: 'change',
|
||||
},
|
||||
};
|
||||
|
||||
const formRef: any = ref(null);
|
||||
const message = useMessage();
|
||||
const { uploadUrl } = globSetting;
|
||||
|
||||
const formValue = reactive({
|
||||
name: '',
|
||||
mobile: '',
|
||||
//图片列表 通常查看和编辑使用 绝对路径 | 相对路径都可以
|
||||
images: ['https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'],
|
||||
});
|
||||
|
||||
const uploadHeaders = reactive({
|
||||
platform: 'miniPrograms',
|
||||
timestamp: new Date().getTime(),
|
||||
token: 'Q6fFCuhc1vkKn5JNFWaCLf6gRAc5n0LQHd08dSnG4qo=',
|
||||
});
|
||||
|
||||
function formSubmit() {
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
message.success('验证成功');
|
||||
} else {
|
||||
message.error('验证失败,请填写完整信息');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
formRef.value.restoreValidation();
|
||||
}
|
||||
|
||||
function uploadChange(list: string[]) {
|
||||
formValue.images = unref(list);
|
||||
}
|
||||
</script>
|
@@ -60,14 +60,12 @@
|
||||
</n-descriptions-item>
|
||||
|
||||
<n-descriptions-item label="状态">
|
||||
<template v-for="(item, key) in formValue?.status" :key="key">
|
||||
<n-tag
|
||||
:type="getOptionTag(options.sys_normal_disable, item)"
|
||||
size="small"
|
||||
class="min-left-space"
|
||||
>{{ getOptionLabel(options.sys_normal_disable, item) }}</n-tag
|
||||
>
|
||||
</template>
|
||||
<n-tag
|
||||
:type="getOptionTag(options.sys_normal_disable, formValue?.status)"
|
||||
size="small"
|
||||
class="min-left-space"
|
||||
>{{ getOptionLabel(options.sys_normal_disable, formValue?.status) }}</n-tag
|
||||
>
|
||||
</n-descriptions-item>
|
||||
|
||||
|
||||
|
@@ -1,7 +0,0 @@
|
||||
<template>
|
||||
<div>监控台</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<style lang="less" scoped></style>
|
@@ -120,7 +120,6 @@
|
||||
NIcon,
|
||||
NTag,
|
||||
NIconWrapper,
|
||||
NAvatar,
|
||||
useMessage,
|
||||
NImage,
|
||||
useDialog,
|
||||
@@ -160,13 +159,25 @@
|
||||
});
|
||||
} else {
|
||||
return h(
|
||||
NAvatar,
|
||||
NIconWrapper,
|
||||
{
|
||||
size: 48,
|
||||
color: '#2D8CF0',
|
||||
borderRadius: 8,
|
||||
},
|
||||
{
|
||||
default: () => h(getIconComponent(row.logo)),
|
||||
default: () =>
|
||||
h(
|
||||
NIcon,
|
||||
{
|
||||
size: 36,
|
||||
style: {
|
||||
marginTop: '-8px',
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () => h(getIconComponent(row.logo)),
|
||||
}
|
||||
),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@
|
||||
HelpCircleOutline,
|
||||
RemoveCircleOutline,
|
||||
} from '@vicons/ionicons5';
|
||||
|
||||
hljs.registerLanguage('goLang', goLang);
|
||||
|
||||
interface Props {
|
||||
|
@@ -1,194 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="基础表单">
|
||||
表单页用于向用户收集或验证信息,基础表单常见于数据项较少的表单场景。表单域标签也可支持响应式。
|
||||
</n-card>
|
||||
</div>
|
||||
<n-card :bordered="false" class="mt-4 proCard">
|
||||
<n-grid cols="1 s:1 m:3 l:3 xl:3 2xl:3" responsive="screen">
|
||||
<n-grid-item offset="0 s:0 m:1 l:1 xl:1 2xl:1">
|
||||
<n-form
|
||||
:label-width="80"
|
||||
:model="formValue"
|
||||
:rules="rules"
|
||||
label-placement="left"
|
||||
ref="formRef"
|
||||
class="py-8"
|
||||
>
|
||||
<n-form-item label="预约姓名1" path="name">
|
||||
<n-input v-model:value="formValue.name" placeholder="输入姓名" />
|
||||
</n-form-item>
|
||||
<n-form-item label="预约号码" path="mobile">
|
||||
<n-input placeholder="电话号码" v-model:value="formValue.mobile" />
|
||||
</n-form-item>
|
||||
<n-form-item label="预约时间" path="datetime">
|
||||
<n-date-picker type="datetime" v-model:value="formValue.datetime" />
|
||||
</n-form-item>
|
||||
<n-form-item label="预约医生" path="doctor">
|
||||
<n-select
|
||||
placeholder="请选择预约医生"
|
||||
:options="doctorList"
|
||||
v-model:value="formValue.doctor"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="预约事项" path="matter">
|
||||
<n-select
|
||||
placeholder="请选择预约事项"
|
||||
:options="matterList"
|
||||
v-model:value="formValue.matter"
|
||||
multiple
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="性别" path="sex">
|
||||
<n-radio-group v-model:value="formValue.sex" name="sex">
|
||||
<n-space>
|
||||
<n-radio :value="1">男</n-radio>
|
||||
<n-radio :value="2">女</n-radio>
|
||||
</n-space>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
<n-form-item label="预约备注" path="remark">
|
||||
<n-input
|
||||
v-model:value="formValue.remark"
|
||||
type="textarea"
|
||||
placeholder="请输入预约备注"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="图片" path="img">
|
||||
<BasicUpload
|
||||
:action="`${uploadUrl}/v1.0/files`"
|
||||
:headers="uploadHeaders"
|
||||
:data="{ type: 0 }"
|
||||
name="files"
|
||||
:width="100"
|
||||
:height="100"
|
||||
@uploadChange="uploadChange"
|
||||
v-model:value="uploadList"
|
||||
helpText="单个文件不超过20MB,最多只能上传10个文件"
|
||||
/>
|
||||
</n-form-item>
|
||||
<div style="margin-left: 80px">
|
||||
<n-space>
|
||||
<n-button type="primary" @click="formSubmit">提交预约</n-button>
|
||||
<n-button @click="resetForm">重置</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref, reactive } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { BasicUpload } from '@/components/Upload';
|
||||
import { useGlobSetting } from '@/hooks/setting';
|
||||
|
||||
const globSetting = useGlobSetting();
|
||||
|
||||
const matterList = [
|
||||
{
|
||||
label: '种牙',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '补牙',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '根管',
|
||||
value: 3,
|
||||
},
|
||||
];
|
||||
|
||||
const doctorList = [
|
||||
{
|
||||
label: '李医生',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '黄医生',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '张医生',
|
||||
value: 3,
|
||||
},
|
||||
];
|
||||
|
||||
const rules = {
|
||||
name: {
|
||||
required: true,
|
||||
message: '请输入预约姓名',
|
||||
trigger: 'blur',
|
||||
},
|
||||
remark: {
|
||||
required: true,
|
||||
message: '请输入预约备注',
|
||||
trigger: 'blur',
|
||||
},
|
||||
mobile: {
|
||||
required: true,
|
||||
message: '请输入预约电话号码',
|
||||
trigger: ['input'],
|
||||
},
|
||||
datetime: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请选择预约时间',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
doctor: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请选择预约医生',
|
||||
trigger: 'change',
|
||||
},
|
||||
};
|
||||
|
||||
const formRef: any = ref(null);
|
||||
const message = useMessage();
|
||||
const { uploadUrl } = globSetting;
|
||||
|
||||
const defaultValueRef = () => ({
|
||||
name: '',
|
||||
mobile: '',
|
||||
remark: '',
|
||||
sex: 1,
|
||||
matter: null,
|
||||
doctor: null,
|
||||
datetime: [],
|
||||
});
|
||||
|
||||
let formValue = reactive(defaultValueRef());
|
||||
const uploadList = ref([
|
||||
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||
]);
|
||||
const uploadHeaders = reactive({
|
||||
platform: 'miniPrograms',
|
||||
timestamp: new Date().getTime(),
|
||||
token: 'Q6fFCuhc1vkKn5JNFWaCLf6gRAc5n0LQHd08dSnG4qo=',
|
||||
});
|
||||
|
||||
function formSubmit() {
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
message.success('验证成功');
|
||||
} else {
|
||||
message.error('验证失败,请填写完整信息');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
formRef.value.restoreValidation();
|
||||
formValue = Object.assign(unref(formValue), defaultValueRef());
|
||||
}
|
||||
|
||||
function uploadChange(list: string[]) {
|
||||
console.log(list);
|
||||
}
|
||||
</script>
|
@@ -1,124 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="表单详情">
|
||||
表单除了提交数据,有时也用于显示只读信息。
|
||||
</n-card>
|
||||
</div>
|
||||
<n-card
|
||||
:bordered="false"
|
||||
title="基本信息"
|
||||
class="mt-4 proCard"
|
||||
size="small"
|
||||
:segmented="{ content: true }"
|
||||
>
|
||||
<n-descriptions label-placement="left" class="py-2">
|
||||
<n-descriptions-item>
|
||||
<template #label>收款人姓名</template>
|
||||
啊俊
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="收款账户">NaiveUiAdmin@qq.com</n-descriptions-item>
|
||||
<n-descriptions-item label="付款类型">支付宝</n-descriptions-item>
|
||||
<n-descriptions-item label="付款账户">NaiveUiAdmin@163.com</n-descriptions-item>
|
||||
<n-descriptions-item label="转账金额">¥1980.00</n-descriptions-item>
|
||||
<n-descriptions-item label="状态">
|
||||
<n-tag type="success"> 已到账</n-tag>
|
||||
</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
</n-card>
|
||||
<n-card
|
||||
:bordered="false"
|
||||
title="其它信息"
|
||||
class="mt-4 proCard"
|
||||
size="small"
|
||||
:segmented="{ content: true }"
|
||||
>
|
||||
<n-descriptions label-placement="left" class="py-2">
|
||||
<n-descriptions-item>
|
||||
<template #label>城市</template>
|
||||
深圳
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="性别">男</n-descriptions-item>
|
||||
<n-descriptions-item label="邮箱">NaiveUiAdmin@qq.com</n-descriptions-item>
|
||||
<n-descriptions-item label="地址">广东省深圳市南山区</n-descriptions-item>
|
||||
<n-descriptions-item label="生日">1991-06-04</n-descriptions-item>
|
||||
<n-descriptions-item label="认证">
|
||||
<n-tag type="success"> 已认证</n-tag>
|
||||
</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
</n-card>
|
||||
<n-card
|
||||
:bordered="false"
|
||||
title="表格信息"
|
||||
class="mt-4 proCard"
|
||||
size="small"
|
||||
:segmented="{ content: true }"
|
||||
>
|
||||
<n-table :bordered="false" :single-line="false">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>姓名</th>
|
||||
<th>性别</th>
|
||||
<th>城市</th>
|
||||
<th>生日</th>
|
||||
<th width="150">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Ah jung</td>
|
||||
<td>男</td>
|
||||
<td>深圳</td>
|
||||
<td>1993-11-09</td>
|
||||
<td>
|
||||
<n-space>
|
||||
<n-button size="small" type="error">删除</n-button>
|
||||
<n-button size="small" type="info">查看</n-button>
|
||||
</n-space>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>西门飞雪</td>
|
||||
<td>男</td>
|
||||
<td>广州</td>
|
||||
<td>1991-09-11</td>
|
||||
<td>
|
||||
<n-space>
|
||||
<n-button size="small" type="error">删除</n-button>
|
||||
<n-button size="small" type="info">查看</n-button>
|
||||
</n-space>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>泰坦巨人</td>
|
||||
<td>男</td>
|
||||
<td>北京</td>
|
||||
<td>1990-11-03</td>
|
||||
<td>
|
||||
<n-space>
|
||||
<n-button size="small" type="error">删除</n-button>
|
||||
<n-button size="small" type="info">查看</n-button>
|
||||
</n-space>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>猎魔人</td>
|
||||
<td>女</td>
|
||||
<td>上海</td>
|
||||
<td>1992-03-11</td>
|
||||
<td>
|
||||
<n-space>
|
||||
<n-button size="small" type="error">删除</n-button>
|
||||
<n-button size="small" type="info">查看</n-button>
|
||||
</n-space>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</n-table>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup></script>
|
||||
|
||||
<style lang="less" scoped></style>
|
@@ -1,122 +0,0 @@
|
||||
<template>
|
||||
<n-form
|
||||
:label-width="90"
|
||||
:model="formValue"
|
||||
:rules="rules"
|
||||
label-placement="left"
|
||||
ref="form1Ref"
|
||||
style="max-width: 500px; margin: 40px auto 0 80px"
|
||||
>
|
||||
<n-form-item label="付款账户" path="myAccount">
|
||||
<n-select
|
||||
placeholder="请选择付款账户"
|
||||
:options="myAccountList"
|
||||
v-model:value="formValue.myAccount"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="收款账户" path="account">
|
||||
<n-input-group>
|
||||
<n-select
|
||||
placeholder="请选择"
|
||||
:options="accountTypeList"
|
||||
:style="{ width: '20%' }"
|
||||
v-model:value="formValue.accountType"
|
||||
/>
|
||||
<n-input
|
||||
placeholder="请输入收款账户"
|
||||
:style="{ width: '80%' }"
|
||||
v-model:value="formValue.account"
|
||||
/>
|
||||
</n-input-group>
|
||||
</n-form-item>
|
||||
<n-form-item label="收款人姓名" path="name">
|
||||
<n-input placeholder="请输入收款人姓名" v-model:value="formValue.name" />
|
||||
</n-form-item>
|
||||
<n-form-item label="转账金额" path="money">
|
||||
<n-input placeholder="请输入转账金额" v-model:value="formValue.money">
|
||||
<template #prefix>
|
||||
<span class="text-gray-400">¥</span>
|
||||
</template>
|
||||
</n-input>
|
||||
</n-form-item>
|
||||
<div style="margin-left: 80px">
|
||||
<n-space>
|
||||
<n-button type="primary" @click="formSubmit">下一步</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
</n-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, defineEmits } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
|
||||
const myAccountList = [
|
||||
{
|
||||
label: 'NaiveUiAdmin@163.com',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: 'NaiveUiAdmin@qq.com',
|
||||
value: 2,
|
||||
},
|
||||
];
|
||||
|
||||
const accountTypeList = [
|
||||
{
|
||||
label: '微信',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '支付宝',
|
||||
value: 2,
|
||||
},
|
||||
];
|
||||
|
||||
const emit = defineEmits(['nextStep']);
|
||||
|
||||
const form1Ref: any = ref(null);
|
||||
const message = useMessage();
|
||||
|
||||
const formValue = ref({
|
||||
accountType: 1,
|
||||
myAccount: null,
|
||||
account: 'xioama@qq.com',
|
||||
money: '1980',
|
||||
name: 'Ah jung',
|
||||
});
|
||||
|
||||
const rules = {
|
||||
name: {
|
||||
required: true,
|
||||
message: '请输入收款人姓名',
|
||||
trigger: 'blur',
|
||||
},
|
||||
account: {
|
||||
required: true,
|
||||
message: '请输入收款账户',
|
||||
trigger: 'blur',
|
||||
},
|
||||
money: {
|
||||
required: true,
|
||||
message: '请输入转账金额',
|
||||
trigger: 'blur',
|
||||
},
|
||||
myAccount: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请选择付款账户',
|
||||
trigger: 'change',
|
||||
},
|
||||
};
|
||||
|
||||
function formSubmit() {
|
||||
form1Ref.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
emit('nextStep');
|
||||
} else {
|
||||
message.error('验证失败,请填写完整信息');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
@@ -1,72 +0,0 @@
|
||||
<template>
|
||||
<n-form
|
||||
:label-width="90"
|
||||
:model="formValue"
|
||||
:rules="rules"
|
||||
label-placement="left"
|
||||
ref="form2Ref"
|
||||
style="max-width: 500px; margin: 40px auto 0 80px"
|
||||
>
|
||||
<n-form-item label="付款账户" path="myAccount">
|
||||
<span>NaiveUiAdmin@163.com</span>
|
||||
</n-form-item>
|
||||
<n-form-item label="收款账户" path="account">
|
||||
<span>NaiveUiAdmin@qq.com</span>
|
||||
</n-form-item>
|
||||
<n-form-item label="收款人姓名" path="name">
|
||||
<span>Ah jung</span>
|
||||
</n-form-item>
|
||||
<n-form-item label="转账金额" path="money">
|
||||
<span>¥1980</span>
|
||||
</n-form-item>
|
||||
<n-divider />
|
||||
<n-form-item label="支付密码" path="password">
|
||||
<n-input type="password" v-model:value="formValue.password" />
|
||||
</n-form-item>
|
||||
<div style="margin-left: 80px">
|
||||
<n-space>
|
||||
<n-button type="primary" :loading="loading" @click="formSubmit">提交</n-button>
|
||||
<n-button @click="prevStep">上一步</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
</n-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, defineEmits } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
const form2Ref: any = ref(null);
|
||||
const message = useMessage();
|
||||
const loading = ref(false);
|
||||
|
||||
const formValue = ref({
|
||||
password: '086611',
|
||||
});
|
||||
|
||||
const rules = {
|
||||
password: {
|
||||
required: true,
|
||||
message: '请输入支付密码',
|
||||
trigger: 'blur',
|
||||
},
|
||||
};
|
||||
|
||||
const emit = defineEmits(['prevStep', 'nextStep']);
|
||||
|
||||
function prevStep() {
|
||||
emit('prevStep');
|
||||
}
|
||||
|
||||
function formSubmit() {
|
||||
loading.value = true;
|
||||
form2Ref.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
setTimeout(() => {
|
||||
emit('nextStep');
|
||||
}, 1500);
|
||||
} else {
|
||||
message.error('验证失败,请填写完整信息');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
@@ -1,72 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-result status="success" title="操作成功" description="预计两小时内到账" class="step-result">
|
||||
<template #default>
|
||||
<div class="information">
|
||||
<n-grid cols="2 s:2 m:3 l:3 xl:3 2xl:3" responsive="screen" class="my-1">
|
||||
<n-gi>付款账户:</n-gi>
|
||||
<n-gi>NaiveUiAdmin@163.com</n-gi>
|
||||
</n-grid>
|
||||
<n-grid cols="2 s:2 m:3 l:3 xl:3 2xl:3" responsive="screen" class="my-1">
|
||||
<n-gi>收款账户:</n-gi>
|
||||
<n-gi>xiaoma@qq.com</n-gi>
|
||||
</n-grid>
|
||||
<n-grid cols="2 s:2 m:3 l:3 xl:3 2xl:3" responsive="screen" class="my-1">
|
||||
<n-gi>收款人姓名:</n-gi>
|
||||
<n-gi>啊俊</n-gi>
|
||||
</n-grid>
|
||||
<n-grid cols="2 s:2 m:3 l:3 xl:3 2xl:3" responsive="screen" class="my-1">
|
||||
<n-gi>转账金额:</n-gi>
|
||||
<n-gi>¥<span class="money">1980</span> 元</n-gi>
|
||||
</n-grid>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="flex justify-center">
|
||||
<n-button type="primary" @click="finish" class="mr-4">再转一笔</n-button>
|
||||
<n-button @click="prevStep">查看账单</n-button>
|
||||
</div>
|
||||
</template>
|
||||
</n-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineEmits } from 'vue';
|
||||
|
||||
const emit = defineEmits(['finish', 'prevStep']);
|
||||
function prevStep() {
|
||||
emit('prevStep');
|
||||
}
|
||||
|
||||
function finish() {
|
||||
emit('finish');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.step-result {
|
||||
max-width: 560px;
|
||||
margin: 40px auto 0;
|
||||
|
||||
::v-deep(.n-result-content) {
|
||||
background-color: #fafafa;
|
||||
padding: 24px 40px;
|
||||
}
|
||||
|
||||
.information {
|
||||
line-height: 22px;
|
||||
|
||||
.ant-row:not(:last-child) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.money {
|
||||
font-family: 'Helvetica Neue', sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
line-height: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -1,54 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="分步表单">
|
||||
将一个冗长或用户不熟悉的表单任务分成多个步骤,指导用户完成。
|
||||
</n-card>
|
||||
</div>
|
||||
<n-card :bordered="false" class="mt-4 proCard">
|
||||
<n-space vertical class="steps" justify="center">
|
||||
<n-steps :current="currentTab" :status="currentStatus">
|
||||
<n-step title="填写转账信息" description="确保填写正确" />
|
||||
<n-step title="确认转账信息" description="确认转账信息" />
|
||||
<n-step title="完成转账" description="恭喜您,转账成功" />
|
||||
</n-steps>
|
||||
<step1 v-if="currentTab === 1" @nextStep="nextStep" />
|
||||
<step2 v-if="currentTab === 2" @nextStep="nextStep" @prevStep="prevStep" />
|
||||
<step3 v-if="currentTab === 3" @prevStep="prevStep" @finish="finish" />
|
||||
</n-space>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import step1 from './Step1.vue';
|
||||
import step2 from './Step2.vue';
|
||||
import step3 from './Step3.vue';
|
||||
|
||||
const currentTab = ref(1);
|
||||
const currentStatus = ref('process');
|
||||
|
||||
function nextStep() {
|
||||
if (currentTab.value < 3) {
|
||||
currentTab.value += 1;
|
||||
}
|
||||
}
|
||||
|
||||
function prevStep() {
|
||||
if (currentTab.value > 1) {
|
||||
currentTab.value -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
function finish() {
|
||||
currentTab.value = 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.steps {
|
||||
max-width: 750px;
|
||||
margin: 16px auto;
|
||||
}
|
||||
</style>
|
@@ -1 +0,0 @@
|
||||
<template> 项目文档 </template>
|
@@ -23,8 +23,14 @@
|
||||
column="2"
|
||||
content-style="padding-right: 20px;"
|
||||
>
|
||||
<n-descriptions-item label="用户ID">{{ formValue.id }}</n-descriptions-item>
|
||||
<n-descriptions-item label="管理员ID">{{ formValue.id }}</n-descriptions-item>
|
||||
<n-descriptions-item label="用户名"> {{ formValue.username }} </n-descriptions-item>
|
||||
<n-descriptions-item label="余额">{{
|
||||
Number(formValue.balance).toFixed(2)
|
||||
}}</n-descriptions-item>
|
||||
<n-descriptions-item label="积分">
|
||||
{{ Number(formValue.integral).toFixed(2) }}
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="登录IP">{{
|
||||
formValue.lastLoginIp
|
||||
}}</n-descriptions-item>
|
||||
|
@@ -89,8 +89,8 @@
|
||||
show.value = true;
|
||||
getUserInfo()
|
||||
.then((res) => {
|
||||
res.cash.password = '';
|
||||
formValue.value = res.cash;
|
||||
formValue.value.password = '';
|
||||
})
|
||||
.finally(() => {
|
||||
show.value = false;
|
||||
|
311
web/src/views/home/account/ThirdBind.vue
Normal file
311
web/src/views/home/account/ThirdBind.vue
Normal file
@@ -0,0 +1,311 @@
|
||||
<template>
|
||||
<n-grid cols="1" responsive="screen" class="-mt-5">
|
||||
<n-grid-item>
|
||||
<n-list>
|
||||
<n-list-item>
|
||||
<template #suffix>
|
||||
<n-button type="primary" text @click="openUpdatePassForm">修改</n-button>
|
||||
</template>
|
||||
<n-thing title="绑定微信">
|
||||
<template #description
|
||||
><span class="text-gray-400">已绑定微信号:xxx</span></template
|
||||
>
|
||||
</n-thing>
|
||||
</n-list-item>
|
||||
<n-list-item>
|
||||
<template #suffix>
|
||||
<n-button type="primary" text @click="openUpdateMobileForm">修改</n-button>
|
||||
</template>
|
||||
<n-thing title="绑定抖音">
|
||||
<template #description
|
||||
><span class="text-gray-400"
|
||||
>已绑定抖音号:xxx</span
|
||||
></template
|
||||
>
|
||||
</n-thing>
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
|
||||
<n-modal
|
||||
v-model:show="showModal"
|
||||
:show-icon="false"
|
||||
preset="dialog"
|
||||
title="修改登录密码"
|
||||
:style="{
|
||||
width: dialogWidth,
|
||||
}"
|
||||
>
|
||||
<n-form :label-width="80" :model="formValue" :rules="rules" ref="formRef">
|
||||
<n-form-item label="当前密码" path="oldPassword">
|
||||
<n-input
|
||||
type="password"
|
||||
v-model:value="formValue.oldPassword"
|
||||
placeholder="请输入当前密码"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="新密码" path="newPassword">
|
||||
<n-input type="password" v-model:value="formValue.newPassword" placeholder="请输入新密码" />
|
||||
</n-form-item>
|
||||
|
||||
<div>
|
||||
<n-space justify="end">
|
||||
<n-button @click="showModal = false">取消</n-button>
|
||||
<n-button type="primary" @click="formSubmit">修改并重新登录</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-modal>
|
||||
|
||||
<n-modal
|
||||
:block-scroll="false"
|
||||
:mask-closable="false"
|
||||
v-model:show="showMobileModal"
|
||||
:show-icon="false"
|
||||
preset="dialog"
|
||||
title="修改手机号"
|
||||
:style="{
|
||||
width: dialogWidth,
|
||||
}"
|
||||
>
|
||||
<n-form :label-width="80" :model="formMobileValue" ref="formMobileRef">
|
||||
<n-form-item label="短信验证码" path="code" v-if="userStore.info?.mobile !== ''">
|
||||
<n-input-group>
|
||||
<n-input v-model:value="formMobileValue.code" placeholder="请输入验证码" />
|
||||
<n-button
|
||||
type="primary"
|
||||
ghost
|
||||
@click="sendMobileCode"
|
||||
:disabled="isCounting"
|
||||
:loading="sendLoading"
|
||||
>
|
||||
{{ sendLabel }}
|
||||
</n-button>
|
||||
</n-input-group>
|
||||
|
||||
<template #feedback> 接收号码:+86{{ userStore.info?.mobile }} </template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="换绑手机号" path="mobile">
|
||||
<n-input v-model:value="formMobileValue.mobile" placeholder="请输入换绑手机号" />
|
||||
</n-form-item>
|
||||
<div>
|
||||
<n-space justify="end">
|
||||
<n-button @click="showMobileModal = false">取消</n-button>
|
||||
<n-button type="primary" :loading="formMobileBtnLoading" @click="formMobileSubmit"
|
||||
>保存更新</n-button
|
||||
>
|
||||
</n-space>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-modal>
|
||||
|
||||
<n-modal
|
||||
:block-scroll="false"
|
||||
:mask-closable="false"
|
||||
v-model:show="showEmailModal"
|
||||
:show-icon="false"
|
||||
preset="dialog"
|
||||
title="修改邮箱"
|
||||
:style="{
|
||||
width: dialogWidth,
|
||||
}"
|
||||
>
|
||||
<n-form :label-width="80" :model="formEmailValue" ref="formEmailRef">
|
||||
<n-form-item label="邮箱验证码" path="code" v-if="userStore.info?.email !== ''">
|
||||
<n-input-group>
|
||||
<n-input v-model:value="formEmailValue.code" placeholder="请输入验证码" />
|
||||
<n-button
|
||||
type="primary"
|
||||
ghost
|
||||
@click="sendEmailCode"
|
||||
:disabled="isCounting"
|
||||
:loading="sendLoading"
|
||||
>
|
||||
{{ sendLabel }}
|
||||
</n-button>
|
||||
</n-input-group>
|
||||
<template #feedback> 接收邮箱:{{ userStore.info?.email }} </template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="换绑邮箱" path="email">
|
||||
<n-input v-model:value="formEmailValue.email" placeholder="请输入换绑邮箱" />
|
||||
</n-form-item>
|
||||
<div>
|
||||
<n-space justify="end">
|
||||
<n-button @click="showEmailModal = false">取消</n-button>
|
||||
<n-button type="primary" :loading="formEmailBtnLoading" @click="formEmailSubmit"
|
||||
>保存更新</n-button
|
||||
>
|
||||
</n-space>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useSendCode } from '@/hooks/common';
|
||||
import { adaModalWidth } from '@/utils/hotgo';
|
||||
import {
|
||||
updateMemberPwd,
|
||||
updateMemberMobile,
|
||||
updateMemberEmail,
|
||||
SendBindEmail,
|
||||
SendBindSms,
|
||||
} from '@/api/system/user';
|
||||
import { TABS_ROUTES } from '@/store/mutation-types';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
const { sendLabel, isCounting, loading: sendLoading, activateSend } = useSendCode();
|
||||
const userStore = useUserStore();
|
||||
const dialogWidth = ref('75%');
|
||||
const rules = {
|
||||
basicName: {
|
||||
required: true,
|
||||
message: '请输入网站名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
};
|
||||
|
||||
const formRef: any = ref(null);
|
||||
const message = useMessage();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const showModal = ref(false);
|
||||
const formValue = ref({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
});
|
||||
|
||||
function formSubmit() {
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
updateMemberPwd({
|
||||
oldPassword: formValue.value.oldPassword,
|
||||
newPassword: formValue.value.newPassword,
|
||||
})
|
||||
.then((_res) => {
|
||||
message.success('更新成功');
|
||||
|
||||
userStore.logout().then(() => {
|
||||
message.success('成功退出登录');
|
||||
// 移除标签页
|
||||
localStorage.removeItem(TABS_ROUTES);
|
||||
router
|
||||
.replace({
|
||||
name: 'Login',
|
||||
query: {
|
||||
redirect: route.fullPath,
|
||||
},
|
||||
})
|
||||
.finally(() => location.reload());
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
showModal.value = false;
|
||||
});
|
||||
} else {
|
||||
message.error('验证失败,请填写完整信息');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openUpdatePassForm() {
|
||||
message.error('未开放');
|
||||
return
|
||||
showModal.value = true;
|
||||
formValue.value.newPassword = '';
|
||||
formValue.value.oldPassword = '';
|
||||
}
|
||||
|
||||
const formMobileBtnLoading = ref(false);
|
||||
const formMobileRef: any = ref(null);
|
||||
const showMobileModal = ref(false);
|
||||
const formMobileValue = ref({
|
||||
mobile: '',
|
||||
code: '',
|
||||
});
|
||||
|
||||
function formMobileSubmit() {
|
||||
formMobileRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
formMobileBtnLoading.value = true;
|
||||
updateMemberMobile({
|
||||
mobile: formMobileValue.value.mobile,
|
||||
code: formMobileValue.value.code,
|
||||
})
|
||||
.then((_res) => {
|
||||
message.success('更新成功');
|
||||
showMobileModal.value = false;
|
||||
userStore.GetInfo();
|
||||
})
|
||||
.finally(() => {
|
||||
formMobileBtnLoading.value = false;
|
||||
});
|
||||
} else {
|
||||
message.error('验证失败,请填写完整信息');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openUpdateMobileForm() {
|
||||
message.error('未开放');
|
||||
return
|
||||
showMobileModal.value = true;
|
||||
formMobileValue.value.mobile = '';
|
||||
formMobileValue.value.code = '';
|
||||
}
|
||||
|
||||
const formEmailBtnLoading = ref(false);
|
||||
const formEmailRef: any = ref(null);
|
||||
const showEmailModal = ref(false);
|
||||
const formEmailValue = ref({
|
||||
email: '',
|
||||
code: '',
|
||||
});
|
||||
|
||||
function formEmailSubmit() {
|
||||
formEmailRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
formEmailBtnLoading.value = true;
|
||||
updateMemberEmail({
|
||||
email: formEmailValue.value.email,
|
||||
code: formEmailValue.value.code,
|
||||
})
|
||||
.then((_res) => {
|
||||
message.success('更新成功');
|
||||
showEmailModal.value = false;
|
||||
userStore.GetInfo();
|
||||
})
|
||||
.finally(() => {
|
||||
formEmailBtnLoading.value = false;
|
||||
});
|
||||
} else {
|
||||
message.error('验证失败,请填写完整信息');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openUpdateEmailForm() {
|
||||
showEmailModal.value = true;
|
||||
formEmailValue.value.email = '';
|
||||
formEmailValue.value.code = '';
|
||||
}
|
||||
|
||||
function sendMobileCode() {
|
||||
activateSend(SendBindSms());
|
||||
}
|
||||
|
||||
function sendEmailCode() {
|
||||
activateSend(SendBindEmail());
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
adaModalWidth(dialogWidth, 580);
|
||||
});
|
||||
</script>
|
@@ -20,16 +20,23 @@
|
||||
<BasicSetting v-if="type === 1" />
|
||||
<SafetySetting v-if="type === 2" />
|
||||
<CashSetting v-if="type === 3" />
|
||||
<ThirdBind v-if="type === 4" />
|
||||
</n-card>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import BasicSetting from './BasicSetting.vue';
|
||||
import SafetySetting from './SafetySetting.vue';
|
||||
import CashSetting from './CashSetting.vue';
|
||||
import ThirdBind from './ThirdBind.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
const type = ref(1);
|
||||
const typeTitle = ref('基本设置');
|
||||
|
||||
const typeTabList = [
|
||||
{
|
||||
@@ -47,10 +54,29 @@
|
||||
desc: '提现收款账号支付宝设置',
|
||||
key: 3,
|
||||
},
|
||||
{
|
||||
name: '第三方绑定',
|
||||
desc: '第三方快捷登录、消息推送',
|
||||
key: 4,
|
||||
},
|
||||
];
|
||||
|
||||
const type = ref(1);
|
||||
const typeTitle = ref('基本设置');
|
||||
onMounted(() => {
|
||||
if (router.currentRoute.value.query?.type) {
|
||||
setDefaultOption();
|
||||
}
|
||||
});
|
||||
|
||||
function setDefaultOption() {
|
||||
const key = router.currentRoute.value.query.type as unknown as number;
|
||||
if (key !== undefined && key > 0) {
|
||||
for (const item of typeTabList) {
|
||||
if (item.key == key) {
|
||||
switchType(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function switchType(e) {
|
||||
type.value = e.key;
|
||||
|
@@ -10,13 +10,12 @@
|
||||
type="card"
|
||||
class="card-tabs"
|
||||
:value="defaultTab"
|
||||
size="large"
|
||||
animated
|
||||
@before-leave="handleBeforeLeave"
|
||||
>
|
||||
<n-tab-pane name="1" tab="通知"> <List :type="defaultTab" /></n-tab-pane>
|
||||
<n-tab-pane name="2" tab="公告"> <List :type="defaultTab" /> </n-tab-pane>
|
||||
<n-tab-pane name="3" tab="私信"> <List :type="defaultTab" /> </n-tab-pane>
|
||||
<n-tab-pane name="1" tab="系统通知"> <List :type="defaultTab" /></n-tab-pane>
|
||||
<n-tab-pane name="2" tab="系统公告"> <List :type="defaultTab" /> </n-tab-pane>
|
||||
<n-tab-pane name="3" tab="私信消息"> <List :type="defaultTab" /> </n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</div>
|
||||
|
@@ -1,50 +0,0 @@
|
||||
import { h } from 'vue';
|
||||
import { NAvatar } from 'naive-ui';
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: 'id',
|
||||
key: 'id',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
key: 'name',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '头像',
|
||||
key: 'avatar',
|
||||
width: 100,
|
||||
render(row) {
|
||||
return h(NAvatar, {
|
||||
size: 48,
|
||||
src: row.avatar,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '地址',
|
||||
key: 'address',
|
||||
auth: ['basic_list'], // 同时根据权限控制是否显示
|
||||
ifShow: (_column) => {
|
||||
return true; // 根据业务控制是否显示
|
||||
},
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '开始日期',
|
||||
key: 'beginTime',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '结束日期',
|
||||
key: 'endTime',
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'date',
|
||||
width: 100,
|
||||
},
|
||||
];
|
@@ -1,347 +0,0 @@
|
||||
<template>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset">
|
||||
<template #statusSlot="{ model, field }">
|
||||
<n-input v-model:value="model[field]" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
|
||||
<BasicTable
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
:actionColumn="actionColumn"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
:scroll-x="1090"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<n-button type="primary" @click="addTable">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<PlusOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
添加
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
<template #toolbar>
|
||||
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<n-modal v-model:show="showModal" :show-icon="false" preset="dialog" title="添加">
|
||||
<n-form
|
||||
:model="formParams"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
label-placement="left"
|
||||
:label-width="80"
|
||||
class="py-4"
|
||||
>
|
||||
<n-form-item label="名称" path="name">
|
||||
<n-input placeholder="请输入名称" v-model:value="formParams.name" />
|
||||
</n-form-item>
|
||||
<n-form-item label="地址" path="address">
|
||||
<n-input type="textarea" placeholder="请输入地址" v-model:value="formParams.address" />
|
||||
</n-form-item>
|
||||
<n-form-item label="日期" path="date">
|
||||
<n-date-picker type="datetime" placeholder="请选择日期" v-model:value="formParams.date" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="() => (showModal = false)">取消</n-button>
|
||||
<n-button type="info" :loading="formBtnLoading" @click="confirmForm">确定</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { h, reactive, ref } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { BasicForm, FormSchema, useForm } from '@/components/Form/index';
|
||||
import { getTableList } from '@/api/table/list';
|
||||
import { columns } from './columns';
|
||||
import { PlusOutlined } from '@vicons/antd';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const rules = {
|
||||
name: {
|
||||
required: true,
|
||||
trigger: ['blur', 'input'],
|
||||
message: '请输入名称',
|
||||
},
|
||||
address: {
|
||||
required: true,
|
||||
trigger: ['blur', 'input'],
|
||||
message: '请输入地址',
|
||||
},
|
||||
date: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
trigger: ['blur', 'change'],
|
||||
message: '请选择日期',
|
||||
},
|
||||
};
|
||||
|
||||
const schemas: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
labelMessage: '这是一个提示',
|
||||
component: 'NInput',
|
||||
label: '姓名',
|
||||
componentProps: {
|
||||
placeholder: '请输入姓名',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
rules: [{ required: true, message: '请输入姓名', trigger: ['blur'] }],
|
||||
},
|
||||
{
|
||||
field: 'mobile',
|
||||
component: 'NInputNumber',
|
||||
label: '手机',
|
||||
componentProps: {
|
||||
placeholder: '请输入手机号码',
|
||||
showButton: false,
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
component: 'NSelect',
|
||||
label: '类型',
|
||||
componentProps: {
|
||||
placeholder: '请选择类型',
|
||||
options: [
|
||||
{
|
||||
label: '舒适性',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '经济性',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeDate',
|
||||
component: 'NDatePicker',
|
||||
label: '预约时间',
|
||||
defaultValue: 1183135260000,
|
||||
componentProps: {
|
||||
type: 'date',
|
||||
clearable: true,
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeTime',
|
||||
component: 'NTimePicker',
|
||||
label: '停留时间',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '状态',
|
||||
//插槽
|
||||
slot: 'statusSlot',
|
||||
},
|
||||
{
|
||||
field: 'makeProject',
|
||||
component: 'NCheckbox',
|
||||
label: '预约项目',
|
||||
componentProps: {
|
||||
placeholder: '请选择预约项目',
|
||||
options: [
|
||||
{
|
||||
label: '种牙',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '补牙',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '根管',
|
||||
value: 3,
|
||||
},
|
||||
],
|
||||
onUpdateChecked: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'makeSource',
|
||||
component: 'NRadioGroup',
|
||||
label: '来源',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '网上',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '门店',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
onUpdateChecked: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const router = useRouter();
|
||||
const formRef: any = ref(null);
|
||||
const message = useMessage();
|
||||
const actionRef = ref();
|
||||
|
||||
const showModal = ref(false);
|
||||
const formBtnLoading = ref(false);
|
||||
const formParams = reactive({
|
||||
name: '',
|
||||
address: '',
|
||||
date: null,
|
||||
});
|
||||
|
||||
const params = ref({
|
||||
pageSize: 5,
|
||||
name: 'xiaoMa',
|
||||
});
|
||||
|
||||
const actionColumn = reactive({
|
||||
width: 220,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
render(record) {
|
||||
return h(TableAction as any, {
|
||||
style: 'button',
|
||||
actions: [
|
||||
{
|
||||
label: '删除',
|
||||
icon: 'ic:outline-delete-outline',
|
||||
onClick: handleDelete.bind(null, record),
|
||||
// 根据业务控制是否显示 isShow 和 auth 是并且关系
|
||||
ifShow: () => {
|
||||
return true;
|
||||
},
|
||||
// 根据权限控制是否显示: 有权限,会显示,支持多个
|
||||
auth: ['basic_list'],
|
||||
},
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
ifShow: () => {
|
||||
return true;
|
||||
},
|
||||
auth: ['basic_list'],
|
||||
},
|
||||
],
|
||||
dropDownActions: [
|
||||
{
|
||||
label: '启用',
|
||||
key: 'enabled',
|
||||
// 根据业务控制是否显示: 非enable状态的不显示启用按钮
|
||||
ifShow: () => {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '禁用',
|
||||
key: 'disabled',
|
||||
ifShow: () => {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
],
|
||||
select: (key) => {
|
||||
message.info(`您点击了,${key} 按钮`);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const [register, {}] = useForm({
|
||||
gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
|
||||
labelWidth: 80,
|
||||
schemas,
|
||||
});
|
||||
|
||||
function addTable() {
|
||||
showModal.value = true;
|
||||
}
|
||||
|
||||
const loadDataTable = async (res) => {
|
||||
return await getTableList({ ...formParams, ...params.value, ...res });
|
||||
};
|
||||
|
||||
function onCheckedRow(rowKeys) {
|
||||
console.log(rowKeys);
|
||||
}
|
||||
|
||||
function reloadTable() {
|
||||
actionRef.value.reload();
|
||||
}
|
||||
|
||||
function confirmForm(e) {
|
||||
e.preventDefault();
|
||||
formBtnLoading.value = true;
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
message.success('添加成功');
|
||||
setTimeout(() => {
|
||||
showModal.value = false;
|
||||
reloadTable();
|
||||
});
|
||||
} else {
|
||||
message.error('请填写完整信息');
|
||||
}
|
||||
formBtnLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function handleEdit(record: Recordable) {
|
||||
console.log('点击了编辑', record);
|
||||
router.push({ name: 'basic-info', params: { id: record.id } });
|
||||
}
|
||||
|
||||
function handleDelete(record: Recordable) {
|
||||
console.log('点击了删除', record);
|
||||
message.info('点击了删除');
|
||||
}
|
||||
|
||||
function handleSubmit(values: Recordable) {
|
||||
console.log(values);
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
function handleReset(values: Recordable) {
|
||||
console.log(values);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
@@ -1,34 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="基础详情"> 基础详情,有时也用于显示只读信息。 </n-card>
|
||||
</div>
|
||||
<n-card :bordered="false" class="proCard mt-4" size="small" :segmented="{ content: true }">
|
||||
<n-descriptions label-placement="left" class="py-2">
|
||||
<n-descriptions-item>
|
||||
<template #label>收款人姓名</template>
|
||||
啊俊
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="收款账户">NaiveUiAdmin@qq.com</n-descriptions-item>
|
||||
<n-descriptions-item label="付款类型">支付宝</n-descriptions-item>
|
||||
<n-descriptions-item label="付款账户">NaiveUiAdmin@163.com</n-descriptions-item>
|
||||
<n-descriptions-item label="转账金额">¥1980.00</n-descriptions-item>
|
||||
<n-descriptions-item label="状态">
|
||||
<n-tag type="success"> 已到账</n-tag>
|
||||
</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
@@ -97,19 +97,19 @@
|
||||
<div class="flex-initial mx-2">
|
||||
<a href="javascript:">
|
||||
<n-icon size="24" color="#2d8cf0">
|
||||
<LogoGithub />
|
||||
<LogoWechat />
|
||||
</n-icon>
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex-initial mx-2">
|
||||
<a href="javascript:">
|
||||
<n-icon size="24" color="#2d8cf0">
|
||||
<LogoFacebook />
|
||||
<LogoTiktok />
|
||||
</n-icon>
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex-initial" style="margin-left: auto">
|
||||
<a href="javascript:">注册账号</a>
|
||||
<a @click="handleRegister">注册账号</a>
|
||||
</div>
|
||||
</div>
|
||||
</n-form-item>
|
||||
@@ -125,7 +125,7 @@
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { useMessage, useLoadingBar } from 'naive-ui';
|
||||
import { ResultEnum } from '@/enums/httpEnum';
|
||||
import { PersonOutline, LockClosedOutline, LogoGithub, LogoFacebook } from '@vicons/ionicons5';
|
||||
import { PersonOutline, LockClosedOutline, LogoWechat, LogoTiktok } from '@vicons/ionicons5';
|
||||
import { PageEnum } from '@/enums/pageEnum';
|
||||
import { SafetyCertificateOutlined } from '@vicons/antd';
|
||||
import { GetCaptcha } from '@/api/base';
|
||||
@@ -163,7 +163,6 @@
|
||||
};
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
@@ -213,7 +212,13 @@
|
||||
setTimeout(function () {
|
||||
refreshCode();
|
||||
});
|
||||
console.log('window.location.href',route.path);
|
||||
});
|
||||
|
||||
function handleRegister() {
|
||||
message.success('即将开放,请稍后');
|
||||
return;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@@ -122,7 +122,7 @@
|
||||
const showModal = ref(false);
|
||||
|
||||
const actionColumn = reactive({
|
||||
width: 120,
|
||||
width: 180,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
|
@@ -1,14 +1,13 @@
|
||||
import { h, ref } from 'vue';
|
||||
import { NAvatar, NImage, NTag, NSwitch, NRate, NButton } from 'naive-ui';
|
||||
import { NTag, NButton } from 'naive-ui';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { FormSchema } from '@/components/Form';
|
||||
import { Dicts } from '@/api/dict/dict';
|
||||
|
||||
import { isArray, isNullObject } from '@/utils/is';
|
||||
import { getFileExt } from '@/utils/urlUtils';
|
||||
import { defRangeShortcuts, defShortcuts, formatToDate } from '@/utils/dateUtil';
|
||||
import { isNullObject } from '@/utils/is';
|
||||
import { defRangeShortcuts } from '@/utils/dateUtil';
|
||||
import { format } from 'date-fns';
|
||||
import { getOptionLabel, getOptionTag, Options, errorImg } from '@/utils/hotgo';
|
||||
import { getOptionLabel, getOptionTag, Options } from '@/utils/hotgo';
|
||||
import { renderIcon, renderTooltip } from '@/utils';
|
||||
import { HelpCircleOutline } from '@vicons/ionicons5';
|
||||
|
||||
|
147
web/src/views/org/user/addBalance.vue
Normal file
147
web/src/views/org/user/addBalance.vue
Normal file
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-spin :show="loading" description="请稍候...">
|
||||
<n-modal
|
||||
v-model:show="isShowModal"
|
||||
:show-icon="false"
|
||||
preset="dialog"
|
||||
title="变更余额"
|
||||
:style="{
|
||||
width: dialogWidth,
|
||||
}"
|
||||
>
|
||||
<n-alert :show-icon="false" type="info">
|
||||
通过扣除或增加你的余额来为
|
||||
<b> {{ params.realName }}</b> 加款或扣款。当扣款方余额不足时,则会操作失败
|
||||
</n-alert>
|
||||
<n-form
|
||||
:model="params"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
label-placement="left"
|
||||
:label-width="80"
|
||||
class="py-4"
|
||||
>
|
||||
<n-form-item label="管理员ID" path="id">
|
||||
<n-input v-model:value="params.id" :disabled="true" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="TA的余额" path="balance">
|
||||
<n-input placeholder="请输入" v-model:value="params.balance" :disabled="true" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="操作方式" path="operateMode">
|
||||
<n-radio-group v-model:value="params.operateMode" name="operateMode">
|
||||
<n-radio-button
|
||||
v-for="status in operateModes"
|
||||
:key="status.value"
|
||||
:value="status.value"
|
||||
:label="status.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="操作数量" path="num">
|
||||
<n-input placeholder="请输入操作数量" v-model:value="params.num" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="closeForm">取消</n-button>
|
||||
<n-button type="info" :loading="formBtnLoading" @click="confirmForm">确定</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, computed, watch } from 'vue';
|
||||
import {
|
||||
addRules as rules,
|
||||
addState as State,
|
||||
addNewState as newState,
|
||||
operateModes,
|
||||
} from './model';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { adaModalWidth } from '@/utils/hotgo';
|
||||
import { GetMemberView, AddMemberBalance } from '@/api/org/user';
|
||||
const emit = defineEmits(['reloadTable', 'updateShowModal']);
|
||||
|
||||
interface Props {
|
||||
showModal: boolean;
|
||||
formParams?: State;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
showModal: false,
|
||||
formParams: () => {
|
||||
return newState(null);
|
||||
},
|
||||
});
|
||||
|
||||
const isShowModal = computed({
|
||||
get: () => {
|
||||
return props.showModal;
|
||||
},
|
||||
set: (value) => {
|
||||
emit('updateShowModal', value);
|
||||
},
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const params = ref<State>(props.formParams);
|
||||
const message = useMessage();
|
||||
const formRef = ref<any>({});
|
||||
const dialogWidth = ref('75%');
|
||||
const formBtnLoading = ref(false);
|
||||
|
||||
function confirmForm(e) {
|
||||
e.preventDefault();
|
||||
formBtnLoading.value = true;
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
AddMemberBalance(params.value).then((_res) => {
|
||||
message.success('操作成功');
|
||||
setTimeout(() => {
|
||||
isShowModal.value = false;
|
||||
emit('reloadTable');
|
||||
});
|
||||
});
|
||||
} else {
|
||||
message.error('请填写完整信息');
|
||||
}
|
||||
formBtnLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
adaModalWidth(dialogWidth);
|
||||
});
|
||||
|
||||
function closeForm() {
|
||||
isShowModal.value = false;
|
||||
}
|
||||
|
||||
function loadForm(value) {
|
||||
loading.value = true;
|
||||
GetMemberView({ id: value.id })
|
||||
.then((res) => {
|
||||
params.value = res;
|
||||
params.value.operateMode = 1;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.formParams,
|
||||
(value) => {
|
||||
loadForm(value);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less"></style>
|
147
web/src/views/org/user/addIntegral.vue
Normal file
147
web/src/views/org/user/addIntegral.vue
Normal file
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-spin :show="loading" description="请稍候...">
|
||||
<n-modal
|
||||
v-model:show="isShowModal"
|
||||
:show-icon="false"
|
||||
preset="dialog"
|
||||
title="变更积分"
|
||||
:style="{
|
||||
width: dialogWidth,
|
||||
}"
|
||||
>
|
||||
<n-alert :show-icon="false" type="info">
|
||||
通过扣除或增加你的积分来为
|
||||
<b> {{ params.realName }}</b> 加款或扣款。当扣款方积分不足时,则会操作失败
|
||||
</n-alert>
|
||||
<n-form
|
||||
:model="params"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
label-placement="left"
|
||||
:label-width="80"
|
||||
class="py-4"
|
||||
>
|
||||
<n-form-item label="管理员ID" path="id">
|
||||
<n-input v-model:value="params.id" :disabled="true" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="TA的积分" path="integral">
|
||||
<n-input placeholder="请输入" v-model:value="params.integral" :disabled="true" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="操作方式" path="operateMode">
|
||||
<n-radio-group v-model:value="params.operateMode" name="operateMode">
|
||||
<n-radio-button
|
||||
v-for="status in operateModes"
|
||||
:key="status.value"
|
||||
:value="status.value"
|
||||
:label="status.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="操作数量" path="num">
|
||||
<n-input placeholder="请输入操作数量" v-model:value="params.num" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="closeForm">取消</n-button>
|
||||
<n-button type="info" :loading="formBtnLoading" @click="confirmForm">确定</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, computed, watch } from 'vue';
|
||||
import {
|
||||
addRules as rules,
|
||||
addState as State,
|
||||
addNewState as newState,
|
||||
operateModes,
|
||||
} from './model';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { adaModalWidth } from '@/utils/hotgo';
|
||||
import { GetMemberView, AddMemberIntegral } from '@/api/org/user';
|
||||
const emit = defineEmits(['reloadTable', 'updateShowModal']);
|
||||
|
||||
interface Props {
|
||||
showModal: boolean;
|
||||
formParams?: State;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
showModal: false,
|
||||
formParams: () => {
|
||||
return newState(null);
|
||||
},
|
||||
});
|
||||
|
||||
const isShowModal = computed({
|
||||
get: () => {
|
||||
return props.showModal;
|
||||
},
|
||||
set: (value) => {
|
||||
emit('updateShowModal', value);
|
||||
},
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const params = ref<State>(props.formParams);
|
||||
const message = useMessage();
|
||||
const formRef = ref<any>({});
|
||||
const dialogWidth = ref('75%');
|
||||
const formBtnLoading = ref(false);
|
||||
|
||||
function confirmForm(e) {
|
||||
e.preventDefault();
|
||||
formBtnLoading.value = true;
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
AddMemberIntegral(params.value).then((_res) => {
|
||||
message.success('操作成功');
|
||||
setTimeout(() => {
|
||||
isShowModal.value = false;
|
||||
emit('reloadTable');
|
||||
});
|
||||
});
|
||||
} else {
|
||||
message.error('请填写完整信息');
|
||||
}
|
||||
formBtnLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
adaModalWidth(dialogWidth);
|
||||
});
|
||||
|
||||
function closeForm() {
|
||||
isShowModal.value = false;
|
||||
}
|
||||
|
||||
function loadForm(value) {
|
||||
loading.value = true;
|
||||
GetMemberView({ id: value.id })
|
||||
.then((res) => {
|
||||
params.value = res;
|
||||
params.value.operateMode = 1;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.formParams,
|
||||
(value) => {
|
||||
loadForm(value);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less"></style>
|
@@ -4,9 +4,9 @@ import { formatBefore } from '@/utils/dateUtil';
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
title: '管理员ID',
|
||||
key: 'id',
|
||||
width: 60,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
@@ -21,7 +21,7 @@ export const columns = [
|
||||
{
|
||||
title: '头像',
|
||||
key: 'avatar',
|
||||
width: 50,
|
||||
width: 70,
|
||||
render(row) {
|
||||
if (row.avatar !== '') {
|
||||
return h(NAvatar, {
|
||||
@@ -84,6 +84,22 @@ export const columns = [
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '余额',
|
||||
key: 'balance',
|
||||
width: 120,
|
||||
render(row) {
|
||||
return '¥' + Number(row.balance).toFixed(2);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '积分',
|
||||
key: 'integral',
|
||||
width: 120,
|
||||
render(row) {
|
||||
return Number(row.integral).toFixed(2);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
|
470
web/src/views/org/user/list.vue
Normal file
470
web/src/views/org/user/list.vue
Normal file
@@ -0,0 +1,470 @@
|
||||
<template>
|
||||
<div>
|
||||
<BasicForm
|
||||
@register="register"
|
||||
@submit="handleSubmit"
|
||||
@reset="handleReset"
|
||||
@keyup.enter="handleSubmit"
|
||||
ref="searchFormRef"
|
||||
>
|
||||
<template #statusSlot="{ model, field }">
|
||||
<n-input v-model:value="model[field]" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
|
||||
<BasicTable
|
||||
:openChecked="true"
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
:actionColumn="actionColumn"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
:scroll-x="1800"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<n-button
|
||||
type="primary"
|
||||
@click="addTable"
|
||||
class="min-left-space"
|
||||
v-if="hasPermission(['/member/edit'])"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<PlusOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
添加用户
|
||||
</n-button>
|
||||
<n-button
|
||||
type="error"
|
||||
@click="batchDelete"
|
||||
:disabled="batchDeleteDisabled"
|
||||
class="min-left-space"
|
||||
v-if="hasPermission(['/member/delete'])"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<DeleteOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
批量删除
|
||||
</n-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<n-modal
|
||||
v-model:show="showModal"
|
||||
:show-icon="false"
|
||||
preset="dialog"
|
||||
:title="formParams?.id > 0 ? '编辑用户 #' + formParams?.id : '添加用户'"
|
||||
:style="{
|
||||
width: dialogWidth,
|
||||
}"
|
||||
>
|
||||
<n-form
|
||||
:model="formParams"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
label-placement="left"
|
||||
:label-width="80"
|
||||
class="py-4"
|
||||
>
|
||||
<n-grid x-gap="24" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="姓名" path="realName">
|
||||
<n-input placeholder="请输入姓名" v-model:value="formParams.realName" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="用户名" path="username">
|
||||
<n-input placeholder="请输入登录用户名" v-model:value="formParams.username" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
|
||||
<n-grid x-gap="24" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="绑定角色" path="roleId">
|
||||
<n-tree-select
|
||||
key-field="id"
|
||||
:options="options.role"
|
||||
:default-value="formParams.roleId"
|
||||
:default-expand-all="true"
|
||||
@update:value="handleUpdateRoleValue"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="所属部门" path="deptId">
|
||||
<n-tree-select
|
||||
key-field="id"
|
||||
:options="options.dept"
|
||||
:default-value="formParams.deptId"
|
||||
:default-expand-all="true"
|
||||
@update:value="handleUpdateDeptValue"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
|
||||
<n-grid x-gap="24" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="绑定岗位" path="postIds">
|
||||
<n-select
|
||||
:default-value="formParams.postIds"
|
||||
multiple
|
||||
:options="options.post"
|
||||
@update:value="handleUpdatePostValue"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="密码" path="password">
|
||||
<n-input
|
||||
type="password"
|
||||
:placeholder="formParams.id === 0 ? '请输入' : '不填则不修改'"
|
||||
v-model:value="formParams.password"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-divider title-placement="left">填写更多信息(可选)</n-divider>
|
||||
<n-grid x-gap="24" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="手机号" path="mobile">
|
||||
<n-input placeholder="请输入" v-model:value="formParams.mobile" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="邮箱" path="email">
|
||||
<n-input placeholder="请输入" v-model:value="formParams.email" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
|
||||
<n-grid x-gap="24" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="性别" path="sex">
|
||||
<n-radio-group v-model:value="formParams.sex" name="sex">
|
||||
<n-radio-button
|
||||
v-for="status in sexOptions"
|
||||
:key="status.value"
|
||||
:value="status.value"
|
||||
:label="status.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="状态" path="status">
|
||||
<n-radio-group v-model:value="formParams.status" name="status">
|
||||
<n-radio-button
|
||||
v-for="status in statusOptions"
|
||||
:key="status.value"
|
||||
:value="status.value"
|
||||
:label="status.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
|
||||
<n-form-item label="备注" path="remark">
|
||||
<n-input type="textarea" placeholder="请输入备注" v-model:value="formParams.remark" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="() => (showModal = false)">取消</n-button>
|
||||
<n-button type="info" :loading="formBtnLoading" @click="confirmForm">确定</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
|
||||
<AddBalance
|
||||
@reloadTable="reloadTable"
|
||||
@updateShowModal="updateBalanceShowModal"
|
||||
:showModal="showBalanceModal"
|
||||
:formParams="formParams"
|
||||
/>
|
||||
|
||||
<AddIntegral
|
||||
@reloadTable="reloadTable"
|
||||
@updateShowModal="updateIntegralShowModal"
|
||||
:showModal="showIntegralModal"
|
||||
:formParams="formParams"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { h, reactive, ref } from 'vue';
|
||||
import { SelectOption, TreeSelectOption, useDialog, useMessage } from 'naive-ui';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { BasicForm } from '@/components/Form/index';
|
||||
import { Delete, Edit, List, Status, ResetPwd } from '@/api/org/user';
|
||||
import { columns } from './columns';
|
||||
import { PlusOutlined, DeleteOutlined } from '@vicons/antd';
|
||||
import { sexOptions, statusOptions } from '@/enums/optionsiEnum';
|
||||
import { adaModalWidth } from '@/utils/hotgo';
|
||||
import { getRandomString } from '@/utils/charset';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import AddBalance from './addBalance.vue';
|
||||
import AddIntegral from './addIntegral.vue';
|
||||
import { addNewState, addState, options, register, defaultState } from './model';
|
||||
import { usePermission } from '@/hooks/web/usePermission';
|
||||
|
||||
interface Props {
|
||||
type?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
type: '-1',
|
||||
});
|
||||
|
||||
const rules = {
|
||||
username: {
|
||||
required: true,
|
||||
trigger: ['blur', 'input'],
|
||||
message: '请输入用户名',
|
||||
},
|
||||
};
|
||||
|
||||
const { hasPermission } = usePermission();
|
||||
const showIntegralModal = ref(false);
|
||||
const showBalanceModal = ref(false);
|
||||
const message = useMessage();
|
||||
const actionRef = ref();
|
||||
const dialog = useDialog();
|
||||
const showModal = ref(false);
|
||||
const formBtnLoading = ref(false);
|
||||
const searchFormRef = ref<any>({});
|
||||
const formRef = ref<any>({});
|
||||
const batchDeleteDisabled = ref(true);
|
||||
const checkedIds = ref([]);
|
||||
const dialogWidth = ref('50%');
|
||||
const formParams = ref<any>();
|
||||
|
||||
const actionColumn = reactive({
|
||||
width: 220,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
render(record) {
|
||||
return h(TableAction as any, {
|
||||
style: 'button',
|
||||
actions: [
|
||||
{
|
||||
label: '已启用',
|
||||
onClick: handleStatus.bind(null, record, 2),
|
||||
ifShow: () => {
|
||||
return record.status === 1 && record.id !== 1;
|
||||
},
|
||||
auth: ['/member/status'],
|
||||
},
|
||||
{
|
||||
label: '已禁用',
|
||||
onClick: handleStatus.bind(null, record, 1),
|
||||
ifShow: () => {
|
||||
return record.status === 2 && record.id !== 1;
|
||||
},
|
||||
auth: ['/member/status'],
|
||||
},
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
ifShow: () => {
|
||||
return record.id !== 1;
|
||||
},
|
||||
auth: ['/member/edit'],
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
onClick: handleDelete.bind(null, record),
|
||||
ifShow: () => {
|
||||
return record.id !== 1;
|
||||
},
|
||||
auth: ['/member/delete'],
|
||||
},
|
||||
],
|
||||
dropDownActions:
|
||||
record.id === 1
|
||||
? []
|
||||
: [
|
||||
{
|
||||
label: '重置密码',
|
||||
key: 0,
|
||||
},
|
||||
{
|
||||
label: '变更余额',
|
||||
key: 100,
|
||||
},
|
||||
{
|
||||
label: '变更积分',
|
||||
key: 101,
|
||||
},
|
||||
],
|
||||
select: (key) => {
|
||||
if (key === 0) {
|
||||
return handleResetPwd(record);
|
||||
}
|
||||
if (key === 100) {
|
||||
return handleAddBalance(record);
|
||||
}
|
||||
if (key === 101) {
|
||||
return handleAddIntegral(record);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function addTable() {
|
||||
showModal.value = true;
|
||||
formParams.value = cloneDeep(defaultState);
|
||||
}
|
||||
|
||||
const loadDataTable = async (res) => {
|
||||
adaModalWidth(dialogWidth);
|
||||
return await List({ ...res, ...searchFormRef.value?.formModel, ...{ roleId: props.type } });
|
||||
};
|
||||
|
||||
function onCheckedRow(rowKeys) {
|
||||
batchDeleteDisabled.value = rowKeys.length <= 0;
|
||||
checkedIds.value = rowKeys;
|
||||
}
|
||||
|
||||
function reloadTable() {
|
||||
actionRef.value.reload();
|
||||
}
|
||||
|
||||
function confirmForm(e) {
|
||||
e.preventDefault();
|
||||
formBtnLoading.value = true;
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
Edit(formParams.value).then((_res) => {
|
||||
message.success('操作成功');
|
||||
setTimeout(() => {
|
||||
showModal.value = false;
|
||||
reloadTable();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
message.error('请填写完整信息');
|
||||
}
|
||||
formBtnLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function handleEdit(record: Recordable) {
|
||||
showModal.value = true;
|
||||
formParams.value = cloneDeep(record);
|
||||
}
|
||||
|
||||
function handleResetPwd(record: Recordable) {
|
||||
record.password = getRandomString(12);
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
content: '你确定要重置密码?\r\n重置成功后密码为:' + record.password + '\r\n 请先保存',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
ResetPwd(record).then((_res) => {
|
||||
message.success('操作成功');
|
||||
reloadTable();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleDelete(record: Recordable) {
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
content: '你确定要删除?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
Delete(record).then((_res) => {
|
||||
message.success('操作成功');
|
||||
reloadTable();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function batchDelete() {
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
content: '你确定要删除?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
Delete({ id: checkedIds.value }).then((_res) => {
|
||||
message.success('操作成功');
|
||||
reloadTable();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleSubmit(_values: Recordable) {
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
function handleReset(_values: Recordable) {
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
function handleStatus(record: Recordable, status) {
|
||||
Status({ id: record.id, status: status }).then((_res) => {
|
||||
message.success('操作成功');
|
||||
setTimeout(() => {
|
||||
reloadTable();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function handleUpdateDeptValue(
|
||||
value: string | number | Array<string | number> | null,
|
||||
_option: TreeSelectOption | null | Array<TreeSelectOption | null>
|
||||
) {
|
||||
formParams.value.deptId = Number(value);
|
||||
}
|
||||
|
||||
function handleUpdateRoleValue(
|
||||
value: string | number | Array<string | number> | null,
|
||||
_option: SelectOption | null | Array<SelectOption | null>
|
||||
) {
|
||||
formParams.value.roleId = Number(value);
|
||||
}
|
||||
|
||||
function handleUpdatePostValue(
|
||||
value: string | number | Array<string | number> | null,
|
||||
_option: SelectOption | null | Array<SelectOption | null>
|
||||
) {
|
||||
formParams.value.postIds = value;
|
||||
}
|
||||
|
||||
function updateBalanceShowModal(value) {
|
||||
showBalanceModal.value = value;
|
||||
}
|
||||
|
||||
function handleAddBalance(record: Recordable) {
|
||||
showBalanceModal.value = true;
|
||||
formParams.value = addNewState(record as addState);
|
||||
}
|
||||
|
||||
function updateIntegralShowModal(value) {
|
||||
showIntegralModal.value = value;
|
||||
}
|
||||
|
||||
function handleAddIntegral(record: Recordable) {
|
||||
showIntegralModal.value = true;
|
||||
formParams.value = addNewState(record as addState);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
214
web/src/views/org/user/model.ts
Normal file
214
web/src/views/org/user/model.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { ref } from 'vue';
|
||||
import { getDeptOption } from '@/api/org/dept';
|
||||
import { getRoleOption } from '@/api/system/role';
|
||||
import { getPostOption } from '@/api/org/post';
|
||||
import { FormSchema, useForm } from '@/components/Form';
|
||||
import { statusOptions } from '@/enums/optionsiEnum';
|
||||
import { defRangeShortcuts } from '@/utils/dateUtil';
|
||||
|
||||
// 增加余额/积分.
|
||||
|
||||
export interface addState {
|
||||
id: number;
|
||||
username: string;
|
||||
realName: string;
|
||||
integral: number;
|
||||
balance: number;
|
||||
operateMode: number;
|
||||
num: number | null;
|
||||
}
|
||||
|
||||
export const addDefaultState = {
|
||||
id: 0,
|
||||
realName: '',
|
||||
username: '',
|
||||
integral: 0,
|
||||
balance: 0,
|
||||
operateMode: 1,
|
||||
num: null,
|
||||
};
|
||||
|
||||
export function addNewState(state: addState | null): addState {
|
||||
if (state !== null) {
|
||||
return cloneDeep(state);
|
||||
}
|
||||
return cloneDeep(addDefaultState);
|
||||
}
|
||||
|
||||
export const operateModes = [
|
||||
{
|
||||
value: 1,
|
||||
label: '加款',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '扣款',
|
||||
},
|
||||
];
|
||||
|
||||
export const addRules = {};
|
||||
|
||||
// 用户列表.
|
||||
|
||||
export const defaultState = {
|
||||
id: 0,
|
||||
roleId: null,
|
||||
realName: '',
|
||||
username: '',
|
||||
password: '',
|
||||
deptId: null,
|
||||
postIds: null,
|
||||
mobile: '',
|
||||
email: '',
|
||||
sex: 1,
|
||||
leader: '',
|
||||
phone: '',
|
||||
sort: 0,
|
||||
status: 1,
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
};
|
||||
|
||||
export interface State {
|
||||
id: number;
|
||||
roleId: number | null;
|
||||
realName: string;
|
||||
username: string;
|
||||
password: string;
|
||||
deptId: number | null;
|
||||
postIds: any;
|
||||
mobile: string;
|
||||
email: string;
|
||||
sex: number;
|
||||
leader: string;
|
||||
phone: string;
|
||||
sort: number;
|
||||
status: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
const schemas: FormSchema[] = [
|
||||
{
|
||||
field: 'username',
|
||||
component: 'NInput',
|
||||
label: '用户名',
|
||||
componentProps: {
|
||||
placeholder: '请输入用户名',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
rules: [{ message: '请输入用户名', trigger: ['blur'] }],
|
||||
},
|
||||
{
|
||||
field: 'realName',
|
||||
component: 'NInput',
|
||||
label: '姓名',
|
||||
componentProps: {
|
||||
placeholder: '请输入姓名',
|
||||
showButton: false,
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'mobile',
|
||||
component: 'NInputNumber',
|
||||
label: '手机号',
|
||||
componentProps: {
|
||||
placeholder: '请输入手机号码',
|
||||
showButton: false,
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
component: 'NInput',
|
||||
label: '邮箱',
|
||||
componentProps: {
|
||||
placeholder: '请输入邮箱地址',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
component: 'NSelect',
|
||||
label: '状态',
|
||||
defaultValue: null,
|
||||
componentProps: {
|
||||
placeholder: '请选择类型',
|
||||
options: statusOptions,
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'created_at',
|
||||
component: 'NDatePicker',
|
||||
label: '创建时间',
|
||||
componentProps: {
|
||||
type: 'datetimerange',
|
||||
clearable: true,
|
||||
shortcuts: defRangeShortcuts(),
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const [register, {}] = useForm({
|
||||
gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
|
||||
labelWidth: 80,
|
||||
schemas,
|
||||
});
|
||||
|
||||
export const options = ref<any>({
|
||||
role: [],
|
||||
roleTabs: [{ id: -1, name: '全部' }],
|
||||
dept: [],
|
||||
post: [],
|
||||
});
|
||||
|
||||
async function loadOptions() {
|
||||
const dept = await getDeptOption();
|
||||
if (dept.list !== undefined) {
|
||||
options.value.dept = dept.list;
|
||||
}
|
||||
|
||||
const role = await getRoleOption();
|
||||
if (role.list !== undefined) {
|
||||
options.value.role = role.list;
|
||||
treeDataToCompressed(role.list);
|
||||
}
|
||||
|
||||
const post = await getPostOption();
|
||||
if (post.list !== undefined && post.list.length > 0) {
|
||||
for (let i = 0; i < post.list.length; i++) {
|
||||
post.list[i].label = post.list[i].name;
|
||||
post.list[i].value = post.list[i].id;
|
||||
}
|
||||
options.value.post = post.list;
|
||||
}
|
||||
}
|
||||
|
||||
function treeDataToCompressed(source) {
|
||||
for (const i in source) {
|
||||
options.value.roleTabs.push(source[i]);
|
||||
source[i].children && source[i].children.length > 0
|
||||
? treeDataToCompressed(source[i].children)
|
||||
: ''; // 子级递归
|
||||
}
|
||||
|
||||
return options.value.roleTabs;
|
||||
}
|
||||
|
||||
await loadOptions();
|
@@ -1,545 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-card :bordered="false" class="proCard" title="后台用户">
|
||||
<BasicForm
|
||||
@register="register"
|
||||
@submit="handleSubmit"
|
||||
@reset="handleReset"
|
||||
@keyup.enter="handleSubmit"
|
||||
ref="searchFormRef"
|
||||
<div class="n-layout-page-header">
|
||||
<n-card :bordered="false" title="后台用户" />
|
||||
</div>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<n-tabs
|
||||
type="card"
|
||||
class="card-tabs"
|
||||
:value="defaultTab"
|
||||
animated
|
||||
@before-leave="handleBeforeLeave"
|
||||
>
|
||||
<template #statusSlot="{ model, field }">
|
||||
<n-input v-model:value="model[field]" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
|
||||
<BasicTable
|
||||
:openChecked="true"
|
||||
:columns="columns"
|
||||
:request="loadDataTable"
|
||||
:row-key="(row) => row.id"
|
||||
ref="actionRef"
|
||||
:actionColumn="actionColumn"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
:scroll-x="1090"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<n-button type="primary" @click="addTable" class="min-left-space">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<PlusOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
添加用户
|
||||
</n-button>
|
||||
<n-button
|
||||
type="error"
|
||||
@click="batchDelete"
|
||||
:disabled="batchDeleteDisabled"
|
||||
class="min-left-space"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<DeleteOutlined />
|
||||
</n-icon>
|
||||
</template>
|
||||
批量删除
|
||||
</n-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<n-modal
|
||||
v-model:show="showModal"
|
||||
:show-icon="false"
|
||||
preset="dialog"
|
||||
:title="formParams?.id > 0 ? '编辑用户 #' + formParams?.id : '添加用户'"
|
||||
:style="{
|
||||
width: dialogWidth,
|
||||
}"
|
||||
>
|
||||
<n-form
|
||||
:model="formParams"
|
||||
:rules="rules"
|
||||
ref="formRef"
|
||||
label-placement="left"
|
||||
:label-width="80"
|
||||
class="py-4"
|
||||
<n-tab-pane
|
||||
:name="item.id.toString()"
|
||||
:tab="item.name"
|
||||
v-for="item in options.roleTabs"
|
||||
:key="item.key"
|
||||
>
|
||||
<n-grid x-gap="24" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="姓名" path="realName">
|
||||
<n-input placeholder="请输入姓名" v-model:value="formParams.realName" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="用户名" path="username">
|
||||
<n-input placeholder="请输入登录用户名" v-model:value="formParams.username" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
|
||||
<n-grid x-gap="24" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="绑定角色" path="roleId">
|
||||
<n-tree-select
|
||||
key-field="id"
|
||||
:options="roleList"
|
||||
:default-value="formParams.roleId"
|
||||
:default-expand-all="true"
|
||||
@update:value="handleUpdateRoleValue"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="所属部门" path="deptId">
|
||||
<n-tree-select
|
||||
key-field="id"
|
||||
:options="deptList"
|
||||
:default-value="formParams.deptId"
|
||||
:default-expand-all="true"
|
||||
@update:value="handleUpdateDeptValue"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
|
||||
<n-grid x-gap="24" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="绑定岗位" path="postIds">
|
||||
<n-select
|
||||
:default-value="formParams.postIds"
|
||||
multiple
|
||||
:options="postList"
|
||||
@update:value="handleUpdatePostValue"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="密码" path="password">
|
||||
<n-input
|
||||
type="password"
|
||||
:placeholder="formParams.id === 0 ? '请输入' : '不填则不修改'"
|
||||
v-model:value="formParams.password"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-divider title-placement="left">填写更多信息(可选)</n-divider>
|
||||
<n-grid x-gap="24" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="手机号" path="mobile">
|
||||
<n-input placeholder="请输入" v-model:value="formParams.mobile" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="邮箱" path="email">
|
||||
<n-input placeholder="请输入" v-model:value="formParams.email" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
|
||||
<n-grid x-gap="24" :cols="2">
|
||||
<n-gi>
|
||||
<n-form-item label="性别" path="sex">
|
||||
<n-radio-group v-model:value="formParams.sex" name="sex">
|
||||
<n-radio-button
|
||||
v-for="status in sexOptions"
|
||||
:key="status.value"
|
||||
:value="status.value"
|
||||
:label="status.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-form-item label="状态" path="status">
|
||||
<n-radio-group v-model:value="formParams.status" name="status">
|
||||
<n-radio-button
|
||||
v-for="status in statusOptions"
|
||||
:key="status.value"
|
||||
:value="status.value"
|
||||
:label="status.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
|
||||
<n-form-item label="备注" path="remark">
|
||||
<n-input type="textarea" placeholder="请输入备注" v-model:value="formParams.remark" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="() => (showModal = false)">取消</n-button>
|
||||
<n-button type="info" :loading="formBtnLoading" @click="confirmForm">确定</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
<List :type="defaultTab" />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { h, reactive, ref } from 'vue';
|
||||
import { SelectOption, TreeSelectOption, useDialog, useMessage } from 'naive-ui';
|
||||
import { BasicTable, TableAction } from '@/components/Table';
|
||||
import { BasicForm, FormSchema, useForm } from '@/components/Form/index';
|
||||
import { Delete, Edit, List, Status, ResetPwd } from '@/api/org/user';
|
||||
import { columns } from './columns';
|
||||
import { PlusOutlined, DeleteOutlined } from '@vicons/antd';
|
||||
import { sexOptions, statusOptions } from '@/enums/optionsiEnum';
|
||||
import { getDeptList } from '@/api/org/dept';
|
||||
import { getRoleList } from '@/api/system/role';
|
||||
import { getPostList } from '@/api/org/post';
|
||||
import { adaModalWidth } from '@/utils/hotgo';
|
||||
import { getRandomString } from '@/utils/charset';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { defRangeShortcuts } from '@/utils/dateUtil';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import List from './list.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { options } from './model';
|
||||
|
||||
const params = ref<any>({
|
||||
pageSize: 10,
|
||||
name: '',
|
||||
code: '',
|
||||
status: null,
|
||||
const router = useRouter();
|
||||
const defaultTab = ref('-1');
|
||||
|
||||
onMounted(() => {
|
||||
if (router.currentRoute.value.query?.type) {
|
||||
defaultTab.value = router.currentRoute.value.query.type as string;
|
||||
}
|
||||
});
|
||||
|
||||
const rules = {
|
||||
username: {
|
||||
required: true,
|
||||
trigger: ['blur', 'input'],
|
||||
message: '请输入用户名',
|
||||
},
|
||||
};
|
||||
|
||||
const schemas: FormSchema[] = [
|
||||
{
|
||||
field: 'username',
|
||||
component: 'NInput',
|
||||
label: '用户名',
|
||||
componentProps: {
|
||||
placeholder: '请输入用户名',
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
rules: [{ message: '请输入用户名', trigger: ['blur'] }],
|
||||
},
|
||||
{
|
||||
field: 'realName',
|
||||
component: 'NInput',
|
||||
label: '姓名',
|
||||
componentProps: {
|
||||
placeholder: '请输入姓名',
|
||||
showButton: false,
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'mobile',
|
||||
component: 'NInputNumber',
|
||||
label: '手机号',
|
||||
componentProps: {
|
||||
placeholder: '请输入手机号码',
|
||||
showButton: false,
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
component: 'NInput',
|
||||
label: '邮箱',
|
||||
componentProps: {
|
||||
placeholder: '请输入邮箱地址',
|
||||
onInput: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
component: 'NSelect',
|
||||
label: '状态',
|
||||
defaultValue: null,
|
||||
componentProps: {
|
||||
placeholder: '请选择类型',
|
||||
options: statusOptions,
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'created_at',
|
||||
component: 'NDatePicker',
|
||||
label: '创建时间',
|
||||
componentProps: {
|
||||
type: 'datetimerange',
|
||||
clearable: true,
|
||||
shortcuts: defRangeShortcuts(),
|
||||
onUpdateValue: (e: any) => {
|
||||
console.log(e);
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const message = useMessage();
|
||||
const actionRef = ref();
|
||||
const dialog = useDialog();
|
||||
const showModal = ref(false);
|
||||
const formBtnLoading = ref(false);
|
||||
const searchFormRef = ref<any>({});
|
||||
const formRef = ref<any>({});
|
||||
const batchDeleteDisabled = ref(true);
|
||||
const checkedIds = ref([]);
|
||||
const deptList = ref<any>([]);
|
||||
const roleList = ref<any>([]);
|
||||
const postList = ref<any>([]);
|
||||
const dialogWidth = ref('50%');
|
||||
const defaultState = {
|
||||
id: 0,
|
||||
roleId: null,
|
||||
realName: '',
|
||||
username: '',
|
||||
password: '',
|
||||
deptId: null,
|
||||
postIds: null,
|
||||
mobile: '',
|
||||
email: '',
|
||||
sex: 1,
|
||||
leader: '',
|
||||
phone: '',
|
||||
sort: 0,
|
||||
status: 1,
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
};
|
||||
|
||||
let formParams = ref<any>();
|
||||
|
||||
const actionColumn = reactive({
|
||||
width: 220,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
render(record) {
|
||||
return h(TableAction as any, {
|
||||
style: 'button',
|
||||
actions: [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
onClick: handleDelete.bind(null, record),
|
||||
},
|
||||
],
|
||||
dropDownActions: [
|
||||
{
|
||||
label: '重置密码',
|
||||
key: 0,
|
||||
},
|
||||
{
|
||||
label: '设为启用',
|
||||
key: 1,
|
||||
},
|
||||
{
|
||||
label: '设为禁用',
|
||||
key: 2,
|
||||
},
|
||||
],
|
||||
select: (key) => {
|
||||
if (key === 0) {
|
||||
return handleResetPwd(record);
|
||||
}
|
||||
if (key === 1 || key === 2) {
|
||||
return updateStatus(record.id, key);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const [register, {}] = useForm({
|
||||
gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
|
||||
labelWidth: 80,
|
||||
schemas,
|
||||
});
|
||||
|
||||
function addTable() {
|
||||
showModal.value = true;
|
||||
formParams.value = cloneDeep(defaultState);
|
||||
}
|
||||
|
||||
const loadDataTable = async (res) => {
|
||||
adaModalWidth(dialogWidth);
|
||||
const deptLists = await getDeptList({});
|
||||
deptList.value = deptLists.list;
|
||||
if (deptList.value === undefined || deptList.value === null) {
|
||||
deptList.value = [];
|
||||
}
|
||||
|
||||
roleList.value = [];
|
||||
let roleLists = await getRoleList({ pageSize: 100 });
|
||||
if (roleLists.list === undefined || roleLists.list === null) {
|
||||
roleList.value = [];
|
||||
} else {
|
||||
roleList.value = roleLists.list;
|
||||
}
|
||||
|
||||
postList.value = [];
|
||||
let postLists = await getPostList();
|
||||
if (postLists.list === undefined || postLists.list === null) {
|
||||
postLists = [];
|
||||
} else {
|
||||
postLists = postLists.list;
|
||||
}
|
||||
if (postLists.length > 0) {
|
||||
for (let i = 0; i < postLists.length; i++) {
|
||||
postList.value[i] = {};
|
||||
postList.value[i].label = postLists[i].name;
|
||||
postList.value[i].value = postLists[i].id;
|
||||
}
|
||||
}
|
||||
|
||||
return await List({ ...params.value, ...res, ...searchFormRef.value?.formModel });
|
||||
};
|
||||
|
||||
function onCheckedRow(rowKeys) {
|
||||
batchDeleteDisabled.value = rowKeys.length <= 0;
|
||||
checkedIds.value = rowKeys;
|
||||
}
|
||||
|
||||
function reloadTable() {
|
||||
actionRef.value.reload();
|
||||
}
|
||||
|
||||
function confirmForm(e) {
|
||||
e.preventDefault();
|
||||
formBtnLoading.value = true;
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
Edit(formParams.value).then((_res) => {
|
||||
message.success('操作成功');
|
||||
setTimeout(() => {
|
||||
showModal.value = false;
|
||||
reloadTable();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
message.error('请填写完整信息');
|
||||
}
|
||||
formBtnLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function handleEdit(record: Recordable) {
|
||||
showModal.value = true;
|
||||
formParams.value = cloneDeep(record);
|
||||
}
|
||||
|
||||
function handleResetPwd(record: Recordable) {
|
||||
record.password = getRandomString(12);
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
content: '你确定要重置密码?\r\n重置成功后密码为:' + record.password + '\r\n 请先保存',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
ResetPwd(record).then((_res) => {
|
||||
message.success('操作成功');
|
||||
reloadTable();
|
||||
});
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
// message.error('取消');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleDelete(record: Recordable) {
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
content: '你确定要删除?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
Delete(record).then((_res) => {
|
||||
message.success('操作成功');
|
||||
reloadTable();
|
||||
});
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
// message.error('取消');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function batchDelete() {
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
content: '你确定要删除?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
Delete({ id: checkedIds.value }).then((_res) => {
|
||||
message.success('操作成功');
|
||||
reloadTable();
|
||||
});
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
// message.error('取消');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function handleSubmit(values: Recordable) {
|
||||
console.log(values);
|
||||
params.value = values;
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
function handleReset(values: Recordable) {
|
||||
params.value = values;
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
function updateStatus(id, status) {
|
||||
Status({ id: id, status: status }).then((_res) => {
|
||||
message.success('操作成功');
|
||||
setTimeout(() => {
|
||||
reloadTable();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function handleUpdateDeptValue(
|
||||
value: string | number | Array<string | number> | null,
|
||||
_option: TreeSelectOption | null | Array<TreeSelectOption | null>
|
||||
) {
|
||||
formParams.value.deptId = value;
|
||||
}
|
||||
|
||||
function handleUpdateRoleValue(
|
||||
value: string | number | Array<string | number> | null,
|
||||
_option: SelectOption | null | Array<SelectOption | null>
|
||||
) {
|
||||
formParams.value.roleId = value;
|
||||
}
|
||||
|
||||
function handleUpdatePostValue(
|
||||
value: string | number | Array<string | number> | null,
|
||||
_option: SelectOption | null | Array<SelectOption | null>
|
||||
) {
|
||||
formParams.value.postIds = value;
|
||||
function handleBeforeLeave(tabName: string) {
|
||||
defaultTab.value = tabName;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
@@ -221,6 +221,7 @@
|
||||
<n-gi>
|
||||
<n-form-item label="分配权限" path="permissions">
|
||||
<n-input
|
||||
:type="formParams.permissions.length > 30 ? 'textarea' : ''"
|
||||
placeholder="请输入分配权限,多个权限用,分割"
|
||||
v-model:value="formParams.permissions"
|
||||
/>
|
||||
|
@@ -33,6 +33,7 @@
|
||||
block-line
|
||||
cascade
|
||||
checkable
|
||||
:default-expand-all="true"
|
||||
:virtual-scroll="true"
|
||||
:data="treeData"
|
||||
:expandedKeys="expandedKeys"
|
||||
|
@@ -1,70 +0,0 @@
|
||||
<template>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<div class="result-box">
|
||||
<n-result status="error" title="操作失败" description="请核对并修改以下信息后,再重新提交。">
|
||||
<div class="result-box-extra">
|
||||
<p>您提交的内容有如下错误:</p>
|
||||
<p class="mt-3">
|
||||
<n-space align="center">
|
||||
<n-icon size="20" color="#f0a020">
|
||||
<InfoCircleOutlined />
|
||||
</n-icon>
|
||||
<span>认证照片不够清晰</span>
|
||||
<n-button type="info" text>立即修改</n-button>
|
||||
</n-space>
|
||||
</p>
|
||||
<p class="mt-3">
|
||||
<n-space>
|
||||
<n-icon size="20" color="#f0a020">
|
||||
<InfoCircleOutlined />
|
||||
</n-icon>
|
||||
<span>备注包含敏感字符,并且不能包含政治相关</span>
|
||||
<n-button type="info" text>立即修改</n-button>
|
||||
</n-space>
|
||||
</p>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="flex justify-center mb-4">
|
||||
<n-space align="center">
|
||||
<n-button type="info" @click="goHome">回到首页</n-button>
|
||||
<n-button>查看详情</n-button>
|
||||
<n-button>打印</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
</template>
|
||||
</n-result>
|
||||
</div>
|
||||
</n-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { useThemeVars } from 'naive-ui';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { InfoCircleOutlined } from '@vicons/antd';
|
||||
|
||||
const router = useRouter();
|
||||
const themeVars = useThemeVars();
|
||||
|
||||
const getTableHeaderColor = computed(() => {
|
||||
return themeVars.value.tableHeaderColor;
|
||||
});
|
||||
|
||||
function goHome() {
|
||||
router.push('/');
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.result-box {
|
||||
width: 72%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
padding-top: 5px;
|
||||
|
||||
&-extra {
|
||||
padding: 24px 40px;
|
||||
text-align: left;
|
||||
background: v-bind(getTableHeaderColor);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -1,74 +0,0 @@
|
||||
<template>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<div class="result-box">
|
||||
<n-result
|
||||
status="info"
|
||||
title="提示"
|
||||
description="本次提交,将在24小时候内自动转入对方账户,如操作失误,请及时撤回"
|
||||
>
|
||||
<div class="result-box-extra">
|
||||
<p>您提交的内容如下:</p>
|
||||
<p class="mt-3">
|
||||
<n-space align="center">
|
||||
<n-icon size="20" color="#18a058">
|
||||
<CheckCircleOutlined />
|
||||
</n-icon>
|
||||
<span>转入支付宝账户(189****5426):¥1980元</span>
|
||||
<n-button type="info" text>立即撤回</n-button>
|
||||
</n-space>
|
||||
</p>
|
||||
<p class="mt-3">
|
||||
<n-space>
|
||||
<n-icon size="20" color="#18a058">
|
||||
<CheckCircleOutlined />
|
||||
</n-icon>
|
||||
<span>转入支付宝账户(187****5426):¥2980元</span>
|
||||
<n-button type="info" text>立即撤回</n-button>
|
||||
</n-space>
|
||||
</p>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="flex justify-center mb-4">
|
||||
<n-space align="center">
|
||||
<n-button type="info" @click="goHome">回到首页</n-button>
|
||||
<n-button>查看详情</n-button>
|
||||
<n-button>全部撤回</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
</template>
|
||||
</n-result>
|
||||
</div>
|
||||
</n-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { useThemeVars } from 'naive-ui';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { CheckCircleOutlined } from '@vicons/antd';
|
||||
|
||||
const router = useRouter();
|
||||
const themeVars = useThemeVars();
|
||||
|
||||
const getTableHeaderColor = computed(() => {
|
||||
return themeVars.value.tableHeaderColor;
|
||||
});
|
||||
|
||||
function goHome() {
|
||||
router.push('/');
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.result-box {
|
||||
width: 72%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
padding-top: 5px;
|
||||
|
||||
&-extra {
|
||||
padding: 24px 40px;
|
||||
text-align: left;
|
||||
background: v-bind(getTableHeaderColor);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -1,55 +0,0 @@
|
||||
<template>
|
||||
<n-card :bordered="false" class="proCard">
|
||||
<div class="result-box">
|
||||
<n-result
|
||||
status="success"
|
||||
title="操作成功"
|
||||
description="提交结果页用于反馈一系列操作任务的处理结果,如果仅是简单操作,灰色区域可以显示一些补充的信息。"
|
||||
>
|
||||
<div class="result-box-extra">
|
||||
<p>已提交申请,等待财务部门审核。</p>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="flex justify-center mb-4">
|
||||
<n-space align="center">
|
||||
<n-button type="info" @click="goHome">回到首页</n-button>
|
||||
<n-button>查看详情</n-button>
|
||||
<n-button>打印</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
</template>
|
||||
</n-result>
|
||||
</div>
|
||||
</n-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useThemeVars } from 'naive-ui';
|
||||
|
||||
const router = useRouter();
|
||||
const themeVars = useThemeVars();
|
||||
|
||||
const getTableHeaderColor = computed(() => {
|
||||
return themeVars.value.tableHeaderColor;
|
||||
});
|
||||
|
||||
function goHome() {
|
||||
router.push('/');
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.result-box {
|
||||
width: 72%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
padding-top: 5px;
|
||||
|
||||
&-extra {
|
||||
padding: 24px 40px;
|
||||
text-align: left;
|
||||
background: v-bind(getTableHeaderColor);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -22,6 +22,7 @@
|
||||
:actionColumn="actionColumn"
|
||||
@update:checked-row-keys="onCheckedRow"
|
||||
:scroll-x="1090"
|
||||
:resizeHeightOffset="-10000"
|
||||
>
|
||||
<template #tableTitle>
|
||||
<n-button type="primary" @click="addTable">
|
||||
|
94
web/src/views/system/config/CashSetting.vue
Normal file
94
web/src/views/system/config/CashSetting.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-spin :show="show" description="请稍候...">
|
||||
<n-form :label-width="100" :model="formValue" :rules="rules" ref="formRef">
|
||||
<n-form-item label="申请提现开关" path="cashSwitch">
|
||||
<n-radio-group v-model:value="formValue.cashSwitch" name="cashSwitch">
|
||||
<n-space>
|
||||
<n-radio :value="1">开启</n-radio>
|
||||
<n-radio :value="2">关闭</n-radio>
|
||||
</n-space>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="提现最低手续费(元)" path="cashMinFee">
|
||||
<n-input placeholder="" v-model:value="formValue.cashMinFee" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="提现最低手续费比率" path="cashMinFeeRatio">
|
||||
<n-input placeholder="" v-model:value="formValue.cashMinFeeRatio" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="提现最低金额" path="cashMinMoney">
|
||||
<n-input placeholder="" v-model:value="formValue.cashMinMoney" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="提现提示信息" path="cashTips">
|
||||
<Editor style="height: 320px" v-model:value="formValue.cashTips" />
|
||||
</n-form-item>
|
||||
|
||||
<div>
|
||||
<n-space>
|
||||
<n-button type="primary" @click="formSubmit">保存更新</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { getConfig, updateConfig } from '@/api/sys/config';
|
||||
import Editor from '@/components/Editor/editor.vue';
|
||||
|
||||
const group = ref('cash');
|
||||
const show = ref(false);
|
||||
const rules = {};
|
||||
const formRef: any = ref(null);
|
||||
const message = useMessage();
|
||||
const formValue = ref({
|
||||
cashSwitch: '',
|
||||
cashMinFee: '',
|
||||
cashMinFeeRatio: '',
|
||||
cashMinMoney: 0,
|
||||
cashTips: '',
|
||||
});
|
||||
|
||||
function formSubmit() {
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
updateConfig({ group: group.value, list: formValue.value })
|
||||
.then((_res) => {
|
||||
message.success('更新成功');
|
||||
load();
|
||||
})
|
||||
.catch((error) => {
|
||||
message.error(error.toString());
|
||||
});
|
||||
} else {
|
||||
message.error('验证失败,请填写完整信息');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
load();
|
||||
});
|
||||
|
||||
function load() {
|
||||
show.value = true;
|
||||
new Promise((_resolve, _reject) => {
|
||||
getConfig({ group: group.value })
|
||||
.then((res) => {
|
||||
show.value = false;
|
||||
formValue.value = res.list;
|
||||
})
|
||||
.catch((error) => {
|
||||
show.value = false;
|
||||
message.error(error.toString());
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
248
web/src/views/system/config/PaySetting.vue
Normal file
248
web/src/views/system/config/PaySetting.vue
Normal file
@@ -0,0 +1,248 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-spin :show="show" description="请稍候...">
|
||||
<n-form :label-width="100" :model="formValue" :rules="rules" ref="formRef">
|
||||
<n-form-item label="开启debug" path="payDebug">
|
||||
<n-switch size="large" v-model:value="formValue.payDebug" />
|
||||
<template #feedback>开启后控制台会输出支付相关的日志</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-divider title-placement="left">支付宝</n-divider>
|
||||
<n-alert :show-icon="false" type="info">
|
||||
确保你已经申请开通过支付宝相关产品权限,建议按照以下步骤进行配置
|
||||
<br />1.
|
||||
下载支付宝平台密钥工具(下载地址:https://opendocs.alipay.com/common/02kipk),加签方式选择证书,加密算法选择RSA2
|
||||
<br />2. 生成后的私钥请在工具中转换为PKCS1格式 <br />3.
|
||||
在支付宝中配置证书,参考地址:https://opendocs.alipay.com/common/02khjo?pathHash=5403bedd
|
||||
</n-alert>
|
||||
<n-form-item label="应用ID" path="payAliPayAppId">
|
||||
<n-input v-model:value="formValue.payAliPayAppId" placeholder="" />
|
||||
<template #feedback></template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="应用私钥路径" path="payAliPayPrivateKey">
|
||||
<n-input v-model:value="formValue.payAliPayPrivateKey" placeholder="" clearable />
|
||||
<template #feedback
|
||||
>RSA2 加密算法默认生成格式为 PKCS8,系统默认是RSA2加密,切记转换为 PKCS1 格式</template
|
||||
>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="应用公钥" path="payAliPayAppCertPublicKey">
|
||||
<n-input v-model:value="formValue.payAliPayAppCertPublicKey" placeholder="" clearable />
|
||||
<template #feedback>appCertPublicKey.crt证书路径</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="支付宝根证书路径" path="payAliPayRootCert">
|
||||
<n-input v-model:value="formValue.payAliPayRootCert" placeholder="" clearable />
|
||||
<template #feedback>alipayRootCert.crt证书路径"</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="支付宝公钥证书路径" path="payAliPayCertPublicKeyRSA2">
|
||||
<n-input v-model:value="formValue.payAliPayCertPublicKeyRSA2" placeholder="" clearable />
|
||||
<template #feedback>alipayCertPublicKey_RSA2.crt证书路径"</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-divider title-placement="left">微信支付</n-divider>
|
||||
<n-form-item label="应用ID" path="payWxPayAppId">
|
||||
<n-input v-model:value="formValue.payWxPayAppId" placeholder="" />
|
||||
<template #feedback>和微信配置中的微信公众号配置保持一致</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="商户ID" path="payWxPayMchId">
|
||||
<n-input v-model:value="formValue.payWxPayMchId" placeholder="" />
|
||||
<template #feedback>商户ID 或者服务商模式的 sp_mchid</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="证书序列号" path="payWxPaySerialNo">
|
||||
<n-input v-model:value="formValue.payWxPaySerialNo" placeholder="" />
|
||||
<template #feedback>商户证书的证书序列号</template>
|
||||
</n-form-item>
|
||||
<n-form-item label="APIv3Key" path="payWxPayAPIv3Key">
|
||||
<n-input v-model:value="formValue.payWxPayAPIv3Key" placeholder="" clearable />
|
||||
<template #feedback>商户平台获取</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="私钥" path="payWxPayPrivateKey">
|
||||
<n-input
|
||||
type="textarea"
|
||||
v-model:value="formValue.payWxPayPrivateKey"
|
||||
placeholder=""
|
||||
clearable
|
||||
/>
|
||||
<template #feedback>apiclient_key.pem 读取后的内容</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-divider title-placement="left">QQ支付</n-divider>
|
||||
<n-form-item label="应用ID" path="payQQPayAppId">
|
||||
<n-input v-model:value="formValue.payQQPayAppId" placeholder="" />
|
||||
<template #feedback></template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="商户ID" path="payQQPayMchId">
|
||||
<n-input v-model:value="formValue.payQQPayMchId" placeholder="" />
|
||||
<template #feedback></template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="ApiKey" path="payQQPayApiKey">
|
||||
<n-input
|
||||
type="textarea"
|
||||
v-model:value="formValue.payQQPayApiKey"
|
||||
placeholder=""
|
||||
clearable
|
||||
/>
|
||||
<template #feedback>API秘钥值</template>
|
||||
</n-form-item>
|
||||
|
||||
<div>
|
||||
<n-space>
|
||||
<n-button type="primary" @click="formSubmit">保存更新</n-button>
|
||||
<!-- <n-button type="default" @click="sendTest">测试支付</n-button>-->
|
||||
</n-space>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-spin>
|
||||
|
||||
<n-modal
|
||||
:block-scroll="false"
|
||||
:mask-closable="false"
|
||||
v-model:show="showModal"
|
||||
:show-icon="false"
|
||||
preset="dialog"
|
||||
title="发送测试短信"
|
||||
>
|
||||
<n-form
|
||||
:model="formParams"
|
||||
:rules="rules"
|
||||
ref="formTestRef"
|
||||
label-placement="left"
|
||||
:label-width="80"
|
||||
class="py-4"
|
||||
>
|
||||
<n-form-item label="事件模板" path="event">
|
||||
<n-select :options="options.config_sms_template" v-model:value="formParams.event" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="手机号" path="mobile">
|
||||
<n-input
|
||||
placeholder="请输入接收手机号"
|
||||
v-model:value="formParams.mobile"
|
||||
:required="true"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="验证码" path="code">
|
||||
<n-input
|
||||
placeholder="请输入要接收的验证码"
|
||||
v-model:value="formParams.code"
|
||||
:required="true"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="() => (showModal = false)">关闭</n-button>
|
||||
<n-button type="info" :loading="formBtnLoading" @click="confirmForm">发送</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { getConfig, sendTestSms, updateConfig } from '@/api/sys/config';
|
||||
import { Dicts } from '@/api/dict/dict';
|
||||
import { Options } from '@/utils/hotgo';
|
||||
|
||||
const group = ref('pay');
|
||||
const show = ref(false);
|
||||
const showModal = ref(false);
|
||||
const formBtnLoading = ref(false);
|
||||
const formParams = ref({ mobile: '', event: '', code: '1234' });
|
||||
const rules = {};
|
||||
const formTestRef = ref<any>();
|
||||
const formRef: any = ref(null);
|
||||
const message = useMessage();
|
||||
|
||||
const options = ref<Options>({
|
||||
config_sms_template: [],
|
||||
config_sms_drive: [],
|
||||
});
|
||||
|
||||
const formValue = ref({
|
||||
payDebug: true,
|
||||
payAliPayAppId: '',
|
||||
payAliPayPrivateKey: '',
|
||||
payAliPayAppCertPublicKey: '',
|
||||
payAliPayRootCert: '',
|
||||
payAliPayCertPublicKeyRSA2: '',
|
||||
payWxPayAppId: '',
|
||||
payWxPayMchId: '',
|
||||
payWxPaySerialNo: '',
|
||||
payWxPayAPIv3Key: '',
|
||||
payWxPayPrivateKey: '',
|
||||
payQQPayAppId: '',
|
||||
payQQPayMchId: '',
|
||||
payQQPayApiKey: '',
|
||||
});
|
||||
|
||||
function sendTest() {
|
||||
showModal.value = true;
|
||||
formBtnLoading.value = false;
|
||||
}
|
||||
|
||||
function formSubmit() {
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
updateConfig({ group: group.value, list: formValue.value }).then((_res) => {
|
||||
message.success('更新成功');
|
||||
load();
|
||||
});
|
||||
} else {
|
||||
message.error('验证失败,请填写完整信息');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
load();
|
||||
});
|
||||
|
||||
async function load() {
|
||||
show.value = true;
|
||||
await loadOptions();
|
||||
new Promise((_resolve, _reject) => {
|
||||
getConfig({ group: group.value })
|
||||
.then((res) => {
|
||||
formValue.value = res.list;
|
||||
})
|
||||
.finally(() => {
|
||||
show.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function loadOptions() {
|
||||
options.value = await Dicts({
|
||||
types: ['config_sms_template', 'config_sms_drive'],
|
||||
});
|
||||
}
|
||||
|
||||
function confirmForm(e) {
|
||||
e.preventDefault();
|
||||
formBtnLoading.value = true;
|
||||
formTestRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
sendTestSms(formParams.value).then((_res) => {
|
||||
message.success('发送成功');
|
||||
showModal.value = false;
|
||||
});
|
||||
} else {
|
||||
message.error('请填写完整信息');
|
||||
}
|
||||
formBtnLoading.value = false;
|
||||
});
|
||||
}
|
||||
</script>
|
121
web/src/views/system/config/WechatSetting.vue
Normal file
121
web/src/views/system/config/WechatSetting.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<div>
|
||||
<n-spin :show="show" description="请稍候...">
|
||||
<n-form :label-width="100" :model="formValue" :rules="rules" ref="formRef">
|
||||
<n-divider title-placement="left">公众号</n-divider>
|
||||
<n-form-item label="AppID" path="officialAccountAppId">
|
||||
<n-input v-model:value="formValue.officialAccountAppId" placeholder="" />
|
||||
<template #feedback>请填写微信公众平台后台的AppId</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="AppSecret" path="officialAccountAppSecret">
|
||||
<n-input v-model:value="formValue.officialAccountAppSecret" placeholder="" clearable />
|
||||
<template #feedback>请填写微信公众平台后台的AppSecret</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="Token" path="officialAccountToken">
|
||||
<n-input v-model:value="formValue.officialAccountToken" placeholder="" clearable />
|
||||
<template #feedback
|
||||
>与公众平台接入设置值一致,必须为英文或者数字,长度为3到32个字符</template
|
||||
>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="EncodingAESKey" path="officialAccountEncodingAESKey">
|
||||
<n-input
|
||||
v-model:value="formValue.officialAccountEncodingAESKey"
|
||||
placeholder=""
|
||||
clearable
|
||||
/>
|
||||
<template #feedback
|
||||
>与公众平台接入设置值一致,必须为英文或者数字,长度为43个字符 </template
|
||||
>
|
||||
</n-form-item>
|
||||
|
||||
<n-divider title-placement="left">开放平台</n-divider>
|
||||
<n-form-item label="AppID" path="openPlatformAppId">
|
||||
<n-input v-model:value="formValue.openPlatformAppId" placeholder="" />
|
||||
<template #feedback>请填写微信开放平台后台的AppId</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="AppSecret" path="openPlatformAppSecret">
|
||||
<n-input v-model:value="formValue.openPlatformAppSecret" placeholder="" clearable />
|
||||
<template #feedback>请填写微信开放平台后台的AppSecret</template>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="Token" path="openPlatformToken">
|
||||
<n-input v-model:value="formValue.openPlatformToken" placeholder="" clearable />
|
||||
<template #feedback
|
||||
>与开放平台接入设置值一致,必须为英文或者数字,长度为3到32个字符</template
|
||||
>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="EncodingAESKey" path="openPlatformEncodingAESKey">
|
||||
<n-input v-model:value="formValue.openPlatformEncodingAESKey" placeholder="" clearable />
|
||||
<template #feedback
|
||||
>与开放平台接入设置值一致,必须为英文或者数字,长度为43个字符</template
|
||||
>
|
||||
</n-form-item>
|
||||
|
||||
<!-- <n-divider title-placement="left">小程序</n-divider>-->
|
||||
|
||||
<div>
|
||||
<n-space>
|
||||
<n-button type="primary" @click="formSubmit">保存更新</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { getConfig, updateConfig } from '@/api/sys/config';
|
||||
|
||||
const group = ref('wechat');
|
||||
const show = ref(false);
|
||||
const rules = {};
|
||||
const formRef: any = ref(null);
|
||||
const message = useMessage();
|
||||
const formValue = ref({
|
||||
officialAccountAppId: '',
|
||||
officialAccountAppSecret: '',
|
||||
officialAccountToken: '',
|
||||
officialAccountEncodingAESKey: '',
|
||||
openPlatformAppId: '',
|
||||
openPlatformAppSecret: '',
|
||||
openPlatformToken: '',
|
||||
openPlatformEncodingAESKey: '',
|
||||
});
|
||||
|
||||
function formSubmit() {
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
updateConfig({ group: group.value, list: formValue.value }).then((_res) => {
|
||||
message.success('更新成功');
|
||||
load();
|
||||
});
|
||||
} else {
|
||||
message.error('验证失败,请填写完整信息');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
load();
|
||||
});
|
||||
|
||||
async function load() {
|
||||
show.value = true;
|
||||
new Promise((_resolve, _reject) => {
|
||||
getConfig({ group: group.value })
|
||||
.then((res) => {
|
||||
formValue.value = res.list;
|
||||
})
|
||||
.finally(() => {
|
||||
show.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
@@ -22,8 +22,11 @@
|
||||
<RevealSetting v-if="type === 3" />
|
||||
<EmailSetting v-if="type === 4" />
|
||||
<SmsSetting v-if="type === 5" />
|
||||
<CashSetting v-if="type === 7" />
|
||||
<UploadSetting v-if="type === 8" />
|
||||
<GeoSetting v-if="type === 9" />
|
||||
<PaySetting v-if="type === 10" />
|
||||
<WechatSetting v-if="type === 11" />
|
||||
</n-card>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
@@ -35,20 +38,23 @@
|
||||
import RevealSetting from './RevealSetting.vue';
|
||||
import EmailSetting from './EmailSetting.vue';
|
||||
import ThemeSetting from './ThemeSetting.vue';
|
||||
import CashSetting from './CashSetting.vue';
|
||||
import UploadSetting from './UploadSetting.vue';
|
||||
import GeoSetting from './GeoSetting.vue';
|
||||
import SmsSetting from './SmsSetting.vue';
|
||||
import PaySetting from './PaySetting.vue';
|
||||
import WechatSetting from './WechatSetting.vue';
|
||||
const typeTabList = [
|
||||
{
|
||||
name: '基本设置',
|
||||
desc: '系统常规设置',
|
||||
key: 1,
|
||||
},
|
||||
{
|
||||
name: '主题设置',
|
||||
desc: '系统主题设置',
|
||||
key: 2,
|
||||
},
|
||||
// {
|
||||
// name: '主题设置',
|
||||
// desc: '系统主题设置',
|
||||
// key: 2,
|
||||
// },
|
||||
// {
|
||||
// name: '显示设置',
|
||||
// desc: '系统显示设置',
|
||||
@@ -65,15 +71,15 @@
|
||||
key: 5,
|
||||
},
|
||||
// {
|
||||
// name: '下游配置',
|
||||
// name: '管理员配置',
|
||||
// desc: '默认设置和权限屏蔽',
|
||||
// key: 6,
|
||||
// },
|
||||
// {
|
||||
// name: '提现配置',
|
||||
// desc: '提现规则配置',
|
||||
// key: 7,
|
||||
// },
|
||||
{
|
||||
name: '提现配置',
|
||||
desc: '管理员提现规则配置',
|
||||
key: 7,
|
||||
},
|
||||
{
|
||||
name: '云存储',
|
||||
desc: '配置上传文件驱动',
|
||||
@@ -84,6 +90,16 @@
|
||||
desc: '配置地理位置工具',
|
||||
key: 9,
|
||||
},
|
||||
{
|
||||
name: '支付配置',
|
||||
desc: '支付宝/微信/QQ支付配置等',
|
||||
key: 10,
|
||||
},
|
||||
{
|
||||
name: '微信配置',
|
||||
desc: '公众号/开放平台/小程序配置等',
|
||||
key: 11,
|
||||
},
|
||||
];
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@@ -91,9 +107,12 @@
|
||||
RevealSetting,
|
||||
EmailSetting,
|
||||
ThemeSetting,
|
||||
CashSetting,
|
||||
UploadSetting,
|
||||
GeoSetting,
|
||||
SmsSetting,
|
||||
PaySetting,
|
||||
WechatSetting,
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
|
@@ -83,7 +83,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
||||
},
|
||||
},
|
||||
brotliSize: false,
|
||||
chunkSizeWarningLimit: 2000,
|
||||
chunkSizeWarningLimit: 3000,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
Reference in New Issue
Block a user