Commit f539d7a0 by 李宁

1

1 parent e5b81eec
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}
{
"hash": "655e7fe4",
"configHash": "92cf418e",
"lockfileHash": "d600e0a3",
"browserHash": "d2e0d31b",
"optimized": {
"vue": {
"src": "../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js",
"fileHash": "b7821ec5",
"needsInterop": false
},
"pinia": {
"src": "../../node_modules/pinia/dist/pinia.mjs",
"file": "pinia.js",
"fileHash": "a659a454",
"needsInterop": false
},
"element-plus": {
"src": "../../node_modules/element-plus/es/index.mjs",
"file": "element-plus.js",
"fileHash": "13083701",
"needsInterop": false
},
"@element-plus/icons-vue": {
"src": "../../node_modules/@element-plus/icons-vue/dist/index.js",
"file": "@element-plus_icons-vue.js",
"fileHash": "d67fc749",
"needsInterop": false
},
"axios": {
"src": "../../node_modules/axios/index.js",
"file": "axios.js",
"fileHash": "e22e391f",
"needsInterop": false
},
"vue-router": {
"src": "../../node_modules/vue-router/dist/vue-router.mjs",
"file": "vue-router.js",
"fileHash": "30652b08",
"needsInterop": false
}
},
"chunks": {
"chunk-XAE367SZ": {
"file": "chunk-XAE367SZ.js"
},
"chunk-YHMWYXEE": {
"file": "chunk-YHMWYXEE.js"
},
"chunk-G3PMV62Z": {
"file": "chunk-G3PMV62Z.js"
}
}
}
\ No newline at end of file
This diff could not be displayed because it is too large.
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
export {
__commonJS,
__export,
__toESM
};
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
import {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBaseVNode,
createBlock,
createCommentVNode,
createElementBlock,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
} from "./chunk-YHMWYXEE.js";
import "./chunk-G3PMV62Z.js";
export {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBlock,
createCommentVNode,
createElementBlock,
createBaseVNode as createElementVNode,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
};
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}
......@@ -270,6 +270,8 @@ if (props.expandedIds && props.expandedIds.size === 0) {
}
})
}
</script>
<style scoped>
.space-y-1 > * + * {
......
......@@ -360,7 +360,7 @@ const handleSave = async () => {
})
if (response.c === 0) {
handleFilter()
ElMessage.success('创建成功');
ElMessage.success(editingRole.value?'编辑成功':'创建成功');
} else {
ElMessage.error(response.m);
}
......
......@@ -21,36 +21,41 @@
<div class="relative">
<Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-neutral-500 z-10" />
<el-input
v-model="generalSearch"
placeholder="搜索用户姓名/手机号/工号"
v-model="userNameSearch"
placeholder="搜索用户姓名"
class="h-10 search-input text-ellipsis-input"
clearable
/>
</div>
<el-select
v-model="roleFilter"
placeholder="角色"
class="h-10 w-full"
<div class="relative">
<Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-neutral-500 z-10" />
<el-input
v-model="phoneSearch"
placeholder="搜索手机号"
class="h-10 search-input text-ellipsis-input"
clearable
>
<el-option label="全部角色" value="all" />
<el-option
v-for="role in availableRoles"
:key="role.id"
:label="role.name"
:value="role.id"
/>
</el-select>
</div>
<div class="relative">
<Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-neutral-500 z-10" />
<el-input
v-model="codeSearch"
placeholder="搜索工号"
class="h-10 search-input text-ellipsis-input"
clearable
/>
</div>
<el-select
v-model="accountTypeFilter"
placeholder="账号类型"
class="h-10 w-full"
clearable
>
<el-option label="全部类型" value="all" />
<el-option label="地市级" value="地市级" />
<el-option label="区县级" value="区县级" />
<el-option label="一线人员" value="一线人员" />
<el-option label="全部类型" value="" />
<el-option label="地市级" value="1" />
<el-option label="区县级" value="2" />
<el-option label="一线人员" value="3" />
</el-select>
<el-select
v-model="statusFilter"
......@@ -59,16 +64,16 @@
clearable
>
<el-option label="全部状态" value="all" />
<el-option label="正常" value="正常" />
<el-option label="禁用" value="禁用" />
<el-option label="正常" value="1" />
<el-option label="禁用" value="0" />
</el-select>
<el-select
v-model="organizationFilter"
placeholder="所属组织"
placeholder="所属区域"
class="h-10 w-full"
clearable
>
<el-option label="全部组织" value="all" />
<el-option label="全部区域" value="all" />
<el-option
v-for="org in organizationOptions"
:key="org.id"
......@@ -232,7 +237,7 @@
<el-option
v-for="role in availableRoles"
:key="role.id"
:label="`${role.name}${role.description ? ` - ${role.description}` : ''}`"
:label="`${role.roleName}${role.remark ? ` - ${role.remark}` : ''}`"
:value="role.id"
/>
</el-select>
......@@ -254,7 +259,7 @@
<!-- 所属组织(当前登录用户不可修改) -->
<div v-if="!editingUser || !isCurrentUser(editingUser)" class="space-y-4">
<div class="flex items-center justify-between pb-2 border-b border-neutral-200">
<h3 class="text-neutral-900">所属组织 <span class="text-red-500">*</span></h3>
<h3 class="text-neutral-900">所属区域 <span class="text-red-500">*</span></h3>
<el-tag
v-if="formData.organizationId"
type="primary"
......@@ -312,10 +317,11 @@
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { ref, computed, watch ,onMounted,getCurrentInstance} from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Building2, Search } from 'lucide-vue-next'
import OrganizationTree from './OrganizationTree.vue'
const { $api } = getCurrentInstance()!.appContext.config.globalProperties
// 类型定义
export interface User {
id: string
......@@ -360,10 +366,12 @@ const emit = defineEmits<{
// 响应式数据
const isDialogOpen = ref(false)
const editingUser = ref<User | null>(null)
const generalSearch = ref('')
const userNameSearch = ref('')
const phoneSearch = ref('')
const codeSearch = ref('')
const roleFilter = ref<string>('all')
const accountTypeFilter = ref<'all' | '地市级' | '区县级' | '一线人员'>('all')
const statusFilter = ref<'all' | '正常' | '禁用'>('all')
const accountTypeFilter = ref<'' | '1' | '2' | '3'>('')
const statusFilter = ref<'' | '1' | '0'>('')
const organizationFilter = ref<string>('all')
const organizationExpandedIds = ref(new Set<string>())
// 表单数据
......@@ -371,18 +379,47 @@ const formData = ref({
username: '',
realName: '',
phone: '',
accountType: '一线人员' as '地市级' | '区县级' | '一线人员',
accountType: '',
organizationId: '',
roleId: '',
isActive: true
})
// 计算属性
const availableRoles = computed(() =>
props.roles.filter(r => r.status === '启用' && r.name !== '地市主管理员')
)
const availableRoles = ref([])
const filteredUsers = ref([])
const topLevelOrganizations = computed(() =>
props.organizations.filter(o => !o.parentId)
props.organizations.filter(o => o.id)
)
onMounted(() => {
getRoleList()
getAccountList()
})
const getRoleList = async ()=>{
try {
const response = await $api.queryRoleList({})
if (response && response.c === 0) {
availableRoles.value = response.d.filter(item=>{return item.status==1})
}
} catch (error) {
}
}
const getAccountList = async ()=>{
try {
const response = await $api.queryAccountList({
phone: '',
status: '',
})
if (response && response.c === 0) {
filteredUsers.value = response.d
}
} catch (error) {
}
}
const totalUsers = computed(() => props.users.length - 1) // 排除当前登录用户
const hasFilters = computed(() =>
generalSearch.value !== '' ||
......@@ -391,27 +428,7 @@ const hasFilters = computed(() =>
statusFilter.value !== 'all' ||
organizationFilter.value !== 'all'
)
const filteredUsers = computed(() => {
return props.users.filter(user => {
// 过滤掉当前登录用户
if (user.id === props.currentUserId) return false
// 通用搜索:支持搜索用户姓名、手机号、工号
const searchTerm = generalSearch.value.toLowerCase()
let matchGeneral = true
if (searchTerm !== '') {
matchGeneral = user.realName.toLowerCase().includes(searchTerm) ||
(user.phone && user.phone.toLowerCase().includes(searchTerm)) ||
user.username.toLowerCase().includes(searchTerm)
}
const matchRole = roleFilter.value === 'all' || user.roleId === roleFilter.value
const matchAccountType = accountTypeFilter.value === 'all' || user.accountType === accountTypeFilter.value
const matchStatus = statusFilter.value === 'all' || user.status === statusFilter.value
const matchOrganization = organizationFilter.value === 'all' || user.organizationId === organizationFilter.value
return matchGeneral && matchRole && matchAccountType && matchStatus && matchOrganization
})
})
const roleHint = computed(() => {
if (!formData.value.roleId) return ''
......@@ -527,7 +544,7 @@ const isOrganizationSelectable = (orgType: string, roleId: string): boolean => {
return true
}
}
const handleSave = () => {
const handleSave = async () => {
// 表单验证
if (!formData.value.username.trim()) {
ElMessage.error('请输入工号')
......@@ -547,21 +564,22 @@ const handleSave = () => {
ElMessage.error('请输入正确的11位手机号')
return
}
// 当前登录用户不需要验证组织(不可修改)
if (!editingUser.value || !isCurrentUser(editingUser.value)) {
if (!formData.value.organizationId) {
ElMessage.error('请选择所属组织')
if (!formData.value.accountType) {
ElMessage.error('请选择账号类型')
return
}
}
if (!formData.value.roleId) {
ElMessage.error('请选择角色')
return
}
if (!formData.value.accountType) {
ElMessage.error('请选择账号类型')
// 当前登录用户不需要验证组织(不可修改)
if (!editingUser.value || !isCurrentUser(editingUser.value)) {
if (!formData.value.organizationId) {
ElMessage.error('请选择所属区域')
return
}
}
// 检查用户名是否重复
const existingUser = props.users.find(u =>
u.username === formData.value.username.trim() && u.id !== editingUser.value?.id
......@@ -580,16 +598,22 @@ const handleSave = () => {
creatorId: props.currentUserId
}
// 当前登录用户不可修改自己的所属组织
if (!editingUser.value || !isCurrentUser(editingUser.value)) {
userData.organizationId = formData.value.organizationId
}
if (editingUser.value) {
emit('updateUser', editingUser.value.id, userData)
ElMessage.success('用户更新成功')
// if (!editingUser.value || !isCurrentUser(editingUser.value)) {
// userData.organizationId = formData.value.organizationId
// }
const response = await $api.addAndUpdateRole({
})
if (response.c === 0) {
//handleFilter()
ElMessage.success(editingUser.value?'编辑成功':'创建成功');
} else {
emit('addUser', userData)
ElMessage.success('用户创建成功')
ElMessage.error(response.m);
}
isDialogOpen.value = false
}
// 组织下拉选项(扁平化带缩进)
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!