feat(login): 更换登录验证码为图形字符验证码

- 配置文件 website.js 中将 captchaType 从 'behavior' 改为 'image'
- 修改 userlogin.vue,图形验证码图片由输入框内联改为输入框旁独立显示
- 重写 login.scss 样式,实现验证码图片区域带边框和高亮效果
- 确保验证码图片点击可刷新,且支持从后端接口获取数据
- 登录页仍默认显示 IAM 登录,需切换「账号密码登录」显示验证码区域

feat(contract-manage): 优化所属组织搜索栏为树形下拉选择框

- 引入并应用 organization-search mixin 统一管理所属组织搜索
- contract-manage.js、driver.js、transport-ship.js、transport-vehicle.js 中所属组织搜索改为 searchslot 模式
- contract-manage.vue 中新增 renderOrganizationSearch 方法,渲染基于 ElTreeSelect 的树形下拉组件
- 组织树数据加载时自动排除「外部组织」节点,保持搜索选项清晰
- 所有相关所属组织搜索统一样式与交互,提升用户体验

feat(system): 用户和部门视图样式及操作改进

- user.vue 和 userinfo.vue 中 el-tabs 组件添加 border-card 类型,提升标签页视觉效果
- dept.vue 中添加查看、编辑、删除操作入口,绑定对应方法,通过权限控制显示权限按钮
- dept.vue 菜单宽度、对话框宽度及行为配置优化

style(login): 调整登录样式及验证码布局细节

- 调整 .login-code-wrap 及内部元素的 flex 布局,输入框与验证码图片并排显示
- 设置验证码图片容器尺寸、边框及 hover 高亮样式
- 确保验证码图片撑满容器并禁止用户选中

refactor(mixins): 新增 organization-search 统一搜索 mixin

- 实现组织树数据加载、排除外部节点、归一化及扁平化处理
- 提供 renderOrganizationSearch 方法用于渲染统一树形选择搜索框
- 减少重复代码,方便各页面快速接入和统一维护

chore(vehicle): 车辆页面引入 organization-search mixin

- 在车辆视图 vehicle.vue 中混入 organization-search,增强所属组织搜索功能一致性
This commit is contained in:
2026-08-28 15:41:01 +08:00
parent 99d6284a58
commit f7eea88945
14 changed files with 231 additions and 56 deletions
+32 -2
View File
@@ -794,7 +794,7 @@ import { getToken } from '@/utils/auth';
import { downloadFileByUrl, downloadXls } from '@/utils/util';
import BillingPlanEditor from './components/billing-plan-editor.vue';
import PdfPreview from '@/components/pdf-preview/main.vue';
import { ElImageViewer } from 'element-plus';
import { ElImageViewer, ElTreeSelect } from 'element-plus';
import { OpenFileViewer } from '@open-file-viewer/vue';
import { fallbackPlugin, imagePlugin, officePlugin, textPlugin } from '@open-file-viewer/core';
import '@open-file-viewer/core/style.css';
@@ -1684,7 +1684,10 @@ export default {
this.organizationOptions = this.normalizeOrganizationTree(organizationTree);
this.organizationFlatOptions = this.flattenOrganizationTree(this.organizationOptions);
const column = this.findColumn(this.tableOption.column, 'organizationName');
if (column) column.dicData = this.organizationOptions;
if (column) {
column.dicData = this.organizationOptions;
column.renderSearch = scope => this.renderOrganizationSearch(scope);
}
this.syncCurrentOrganizationOption();
})
.finally(() => {
@@ -1748,6 +1751,24 @@ export default {
this.organizationOptions.unshift(item);
this.organizationFlatOptions.unshift(item);
},
renderOrganizationSearch(scope) {
// 搜索区所属组织:树形下拉(与客户档案统一)
return h(ElTreeSelect, {
modelValue: scope.row?.organizationName || '',
'onUpdate:modelValue': value => {
if (scope.row) scope.row.organizationName = value;
},
data: this.organizationOptions,
'node-key': 'id',
'check-strictly': true,
filterable: true,
clearable: true,
'render-after-expand': false,
style: 'width: 100%',
placeholder: '请选择 所属组织',
props: { label: 'label', value: 'id', children: 'children' },
});
},
integerInput(prop, value) {
this.form[prop] = String(value || '').replace(/\D/g, '');
},
@@ -2089,6 +2110,15 @@ export default {
</script>
<style lang="scss" scoped>
.contract-name-cell {
display: inline-block;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: bottom;
}
.contract-manage-page {
&__expiry-search {
display: flex;
+21 -1
View File
@@ -32,6 +32,15 @@
</el-button>
</template>
<template #menu="scope">
<el-link type="primary" @click.stop="handleView(scope.row, scope.index)" v-if="permissionList.viewBtn"
>查看</el-link
>
<el-link type="primary" @click.stop="handleEdit(scope.row, scope.index)" v-if="permissionList.editBtn"
>编辑</el-link
>
<el-link type="danger" @click.stop="handleDel(scope.row, scope.index)" v-if="permissionList.delBtn"
>删除</el-link
>
<el-link
type="primary"
@click.stop="handleAdd(scope.row, scope.index)"
@@ -177,7 +186,9 @@ export default {
border: true,
index: true,
selection: true,
viewBtn: true,
viewBtn: false,
editBtn: false,
delBtn: false,
menuWidth: 320,
dialogWidth: 800,
dialogClickModal: false,
@@ -519,6 +530,15 @@ export default {
isRootDept(row) {
return row && (row.parentId === 0 || String(row.parentId) === '0');
},
handleView(row, index) {
this.$refs.crud.rowView(row, index);
},
handleEdit(row, index) {
this.$refs.crud.rowEdit(row, index);
},
handleDel(row, index) {
this.$refs.crud.rowDel(row, index);
},
handleAdd(row) {
this.parentId = row.id;
this.loadParentDeptCode(row.id);
+1 -1
View File
@@ -204,7 +204,7 @@
</el-row>
</el-form>
</section-card>
<el-tabs class="user-permission-tabs">
<el-tabs type="border-card" class="user-permission-tabs">
<el-tab-pane label="已拥有角色">
<el-table :data="formRoleList" border>
<el-table-column type="index" label="序号" width="70" />
+1 -1
View File
@@ -1,7 +1,7 @@
<template>
<div>
<basic-container>
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
<el-tabs type="border-card" v-model="activeTab" @tab-change="handleTabChange">
<!-- 个人信息 Tab -->
<el-tab-pane label="个人信息" name="info">
<el-form ref="infoForm" :model="infoForm" label-width="auto">
+3
View File
@@ -544,6 +544,7 @@ import {
recognizeBaiduOcr,
} from '@/api/transportCapacity/transport-vehicle';
import { getDeptTree } from '@/api/system/dept';
import organizationSearch from '@/mixins/organization-search';
import { exportBlob } from '@/api/common';
import { getToken } from '@/utils/auth';
import { getUploadHeaders } from '@/utils/upload';
@@ -633,6 +634,7 @@ export default {
ImageUploadField,
VehicleLedger,
},
mixins: [organizationSearch],
data() {
return {
option,
@@ -747,6 +749,7 @@ export default {
},
created() {
this.initDeptTree();
this.loadOrganizationOptions();
},
methods: {
hasPermission(code) {