This commit is contained in:
孟帅
2023-05-10 23:54:50 +08:00
parent bbe655a4d8
commit 49a96750bf
314 changed files with 15138 additions and 6244 deletions

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

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

View File

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

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

View 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();

View File

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