修复字典键值为bool类型和字符串类型时,下拉列表逻辑Bug

pull/44/head
zqx 2024-09-04 19:30:54 +08:00
parent f67b0b2579
commit 689669a9e3
13 changed files with 102 additions and 295 deletions

8
.env
View File

@ -2,19 +2,19 @@
VITE_PORT = 80
# 网站标题
VITE_GLOB_APP_TITLE = 招聘管理系统
VITE_GLOB_APP_TITLE = 芋道管理系统
# 简称,用于配置文件名字 不要出现空格、数字开头等特殊字符
VITE_GLOB_APP_SHORT_NAME = Yudao_Admin
# 租户开关
VITE_GLOB_APP_TENANT_ENABLE = false
VITE_GLOB_APP_TENANT_ENABLE = true
# 验证码的开关
VITE_GLOB_APP_CAPTCHA_ENABLE = false
VITE_GLOB_APP_CAPTCHA_ENABLE = true
# 文档地址的开关
VITE_APP_DOCALERT_ENABLE=false
VITE_APP_DOCALERT_ENABLE=true
# 百度统计
VITE_APP_BAIDU_CODE = eb21166668bf766b9d059a6fd1c10777

15
.vscode/settings.json vendored
View File

@ -61,8 +61,8 @@
"@/": "${workspaceRoot}/src"
},
"eslint.experimental.useFlatConfig": true,
"prettier.enable": true,
"editor.formatOnSave": true,
"prettier.enable": false,
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "never"
@ -94,7 +94,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
@ -116,11 +116,10 @@
},
"[vue]": {
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never",
"source.fixAll.stylelint": "explicit"
},
"editor.defaultFormatter": "Vue.volar"
"source.fixAll.eslint": true,
"source.organizeImports": false,
"source.fixAll.stylelint": true
}
},
"i18n-ally.localesPaths": ["src/locales/lang"],
"i18n-ally.keystyle": "nested",

View File

@ -114,7 +114,7 @@
"postcss": "^8.4.31",
"postcss-html": "^1.5.0",
"postcss-less": "^6.0.0",
"prettier": "^3.3.3",
"prettier": "^3.1.0",
"rimraf": "^5.0.5",
"rollup": "^4.4.0",
"rollup-plugin-visualizer": "^5.9.2",

View File

@ -212,7 +212,7 @@ devDependencies:
specifier: ^6.0.0
version: 6.0.0(postcss@8.4.44)
prettier:
specifier: ^3.3.3
specifier: ^3.1.0
version: 3.3.3
rimraf:
specifier: ^5.0.5

View File

@ -1,22 +0,0 @@
module.exports = {
printWidth: 100, // 每行代码长度默认80
tabWidth: 2, // 每个tab相当于多少个空格默认2ab进行缩进默认false
useTabs: false, // 是否使用tab
semi: false, // 声明结尾使用分号(默认true)
vueIndentScriptAndStyle: false,
singleQuote: true, // 使用单引号默认false
quoteProps: 'as-needed',
bracketSpacing: true, // 对象字面量的大括号间使用空格默认true
trailingComma: 'none', // 多行使用拖尾逗号默认none
jsxSingleQuote: false,
// 箭头函数参数括号 默认avoid 可选 avoid| always
// avoid 能省略括号的时候就省略 例如x => x
// always 总是有括号
arrowParens: 'always',
insertPragma: false,
requirePragma: false,
proseWrap: 'never',
htmlWhitespaceSensitivity: 'strict',
endOfLine: 'auto',
rangeStart: 0
}

View File

@ -17,7 +17,7 @@ const { themeConfig } = storeToRefs(appStore)
const componentSize = computed(() => appStore.getComponentSize)
// Listening to page changes and dynamically changing site titles
useTitle();
useTitle()
</script>
<template>

View File

@ -26,6 +26,7 @@ export interface ApiErrorLogVO {
resultCode: number
createTime: Date
}
export interface ApiErrorLogPageReqVO extends PageParam {
userId?: number
userType?: number
@ -34,6 +35,7 @@ export interface ApiErrorLogPageReqVO extends PageParam {
exceptionTime?: Date[]
processStatus: number
}
export interface ApiErrorLogExportReqVO {
userId?: number
userType?: number

View File

@ -1,31 +0,0 @@
import { defHttp } from '@/utils/http/axios'
// 查询求职者信息列表
export function getApplicantPage(params) {
return defHttp.get({ url: '/recruit/applicant/page', params })
}
// 查询求职者信息详情
export function getApplicant(id: number) {
return defHttp.get({ url: `/recruit/applicant/get?id=${id}` })
}
// 新增求职者信息
export function createApplicant(data) {
return defHttp.post({ url: '/recruit/applicant/create', data })
}
// 修改求职者信息
export function updateApplicant(data) {
return defHttp.put({ url: '/recruit/applicant/update', data })
}
// 删除求职者信息
export function deleteApplicant(id: number) {
return defHttp.delete({ url: `/recruit/applicant/delete?id=${id}` })
}
// 导出求职者信息 Excel
export function exportApplicant(params) {
return defHttp.download({ url: '/recruit/applicant/export-excel', params }, '求职者信息.xls')
}

View File

@ -54,9 +54,9 @@ function determineType(dictType) {
}
export function getDictOptions(dictType: string, valueType?: 'string' | 'number' | 'boolean') {
if(!valueType){
valueType = determineType(dictType);
}
// if(!valueType){
// valueType = determineType(dictType);
// }
const dictOption: DictDataType[] = []
const dictOptions: DictDataType[] = getDictDatas(dictType)
if (dictOptions && dictOptions.length > 0) {

View File

@ -15,7 +15,7 @@ import type { RequestOptions, Result, UploadFileParams } from '@/types/axios'
import { ContentTypeEnum, RequestEnum } from '@/enums/httpEnum'
import { downloadByData } from '@/utils/file/download'
import { useGlobSetting } from '@/hooks/setting'
import { getAccessToken, getRefreshToken, getTenantId, setAccessToken } from '@/utils/auth'
import {getAccessToken, getRefreshToken, getTenantId, setAccessToken} from '@/utils/auth'
export * from './axiosTransform'
@ -146,7 +146,7 @@ export class VAxios {
const refreshTokenRes = await this.refreshToken()
// 2.1 刷新成功,则回放队列的请求 + 当前请求
setAccessToken(refreshTokenRes.data.data.accessToken)
; (config as Recordable).headers.Authorization = `Bearer ${getAccessToken()}`
;(config as Recordable).headers.Authorization = `Bearer ${getAccessToken()}`
requestList.forEach((cb: any) => {
cb()
})
@ -171,7 +171,7 @@ export class VAxios {
// 添加到队列,等待刷新获取到新的令牌
return new Promise((resolve) => {
requestList.push(() => {
; (config as Recordable).headers.Authorization = `Bearer ${getAccessToken()}` // 让每个请求携带自定义token 请根据实际情况自行修改
;(config as Recordable).headers.Authorization = `Bearer ${getAccessToken()}` // 让每个请求携带自定义token 请根据实际情况自行修改
resolve(this.axiosInstance(config))
})
})
@ -297,9 +297,8 @@ export class VAxios {
}
catch (err) {
reject(err || new Error('request error!'))
return
}
// return
return
}
resolve(res as unknown as Promise<T>)
// download file

View File

@ -120,101 +120,110 @@ async function handleLogin(params) {
<template>
<LoginFormTitle v-show="getShow" class="enter-x" />
<Form v-show="getShow" ref="formRef" class="enter-x p-4" :model="formData" :rules="getFormRules"
@keypress.enter="handleLogin">
<Form
v-show="getShow" ref="formRef" class="enter-x p-4" :model="formData" :rules="getFormRules"
@keypress.enter="handleLogin"
>
<FormItem name="tenantName" class="enter-x">
<Input v-if="tenantEnable === 'true'" v-model:value="formData.tenantName" size="large"
:placeholder="t('sys.login.tenantName')" class="fix-auto-fill" />
<Input
v-if="tenantEnable === 'true'"
v-model:value="formData.tenantName"
size="large"
:placeholder="t('sys.login.tenantName')"
class="fix-auto-fill"
/>
</FormItem>
<FormItem name="username" class="enter-x">
<Input v-model:value="formData.username" size="large" :placeholder="t('sys.login.userName')"
class="fix-auto-fill" />
<Input
v-model:value="formData.username" size="large" :placeholder="t('sys.login.userName')"
class="fix-auto-fill"
/>
</FormItem>
<FormItem name="password" class="enter-x">
<InputPassword v-model:value="formData.password" size="large" visibility-toggle
:placeholder="t('sys.login.password')" class="fix-auto-fill" />
<InputPassword
v-model:value="formData.password"
size="large"
visibility-toggle
:placeholder="t('sys.login.password')"
class="fix-auto-fill"
/>
</FormItem>
<Row class="enter-x">
<Col :span="12">
<FormItem>
<!-- No logic, you need to deal with it yourself -->
<Checkbox v-model:checked="rememberMe" size="small">
{{ t('sys.login.rememberMe') }}
</Checkbox>
</FormItem>
<FormItem>
<!-- No logic, you need to deal with it yourself -->
<Checkbox v-model:checked="rememberMe" size="small">
{{ t('sys.login.rememberMe') }}
</Checkbox>
</FormItem>
</Col>
<Col :span="12">
<FormItem :style="{ 'text-align': 'right' }">
<!-- No logic, you need to deal with it yourself -->
<a-button type="link" size="small" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)">
{{ t('sys.login.forgetPassword') }}
</a-button>
</FormItem>
<FormItem :style="{ 'text-align': 'right' }">
<!-- No logic, you need to deal with it yourself -->
<a-button type="link" size="small" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)">
{{ t('sys.login.forgetPassword') }}
</a-button>
</FormItem>
</Col>
</Row>
<FormItem class="enter-x">
<a-button type="primary" size="large" block :loading="loading" @click="getCode">
{{ t('sys.login.loginButton') }}
</a-button>
<!-- <a-button size="large" class="mt-4 enter-x" block @click="handleRegister">
{{ t('sys.login.registerButton') }}
</a-button> -->
{{ t('sys.login.registerButton') }}
</a-button> -->
</FormItem>
<div v-if="false">
<Row class="enter-x" :gutter="[16, 16]">
<Col :md="8" :xs="24">
<a-button block @click="setLoginState(LoginStateEnum.MOBILE)">
{{ t('sys.login.mobileSignInFormTitle') }}
</a-button>
</Col>
<Col :md="8" :xs="24">
<a-button block @click="setLoginState(LoginStateEnum.QR_CODE)">
{{ t('sys.login.qrSignInFormTitle') }}
</a-button>
</Col>
<Col :md="8" :xs="24">
<a-button block @click="setLoginState(LoginStateEnum.REGISTER)">
{{ t('sys.login.registerButton') }}
</a-button>
</Col>
</Row>
<Divider class="enter-x">
{{ t('sys.login.otherSignIn') }}
</Divider>
<div class="enter-x flex justify-evenly" :class="`${prefixCls}-sign-in-way`">
<GithubFilled />
<WechatFilled />
<AlipayCircleFilled />
<!-- <GoogleCircleFilled /> -->
<!-- <TwitterCircleFilled /> -->
</div>
<!-- 萌新必读 -->
<Divider class="enter-x">
萌新必读
</Divider>
<div class="enter-x flex justify-evenly" :class="`${prefixCls}-sign-in-way`">
<a-button href="https://doc.iocoder.cn/" target="_blank" class="w-1/4">
📚开发指南
<Row class="enter-x" :gutter="[16, 16]">
<Col :md="8" :xs="24">
<a-button block @click="setLoginState(LoginStateEnum.MOBILE)">
{{ t('sys.login.mobileSignInFormTitle') }}
</a-button>
<a-button href="https://doc.iocoder.cn/video/" target="_blank" class="w-1/4 pl-1">
🔥视频教程
</Col>
<Col :md="8" :xs="24">
<a-button block @click="setLoginState(LoginStateEnum.QR_CODE)">
{{ t('sys.login.qrSignInFormTitle') }}
</a-button>
<a-button href="https://www.iocoder.cn/Interview/good-collection/" target="_blank" class="w-1/4 pl-1">
面试手册
</Col>
<Col :md="8" :xs="24">
<a-button block @click="setLoginState(LoginStateEnum.REGISTER)">
{{ t('sys.login.registerButton') }}
</a-button>
<a-button href="http://static.yudao.iocoder.cn/mp/xinyu370.jpeg" target="_blank" class="w-1/4 pl-1">
🤝外包咨询
</a-button>
</div>
</Col>
</Row>
<Divider class="enter-x">
{{ t('sys.login.otherSignIn') }}
</Divider>
<div class="enter-x flex justify-evenly" :class="`${prefixCls}-sign-in-way`">
<GithubFilled />
<WechatFilled />
<AlipayCircleFilled />
<!-- <GoogleCircleFilled /> -->
<!-- <TwitterCircleFilled /> -->
</div>
<!-- 萌新必读 -->
<Divider class="enter-x">
萌新必读
</Divider>
<div class="enter-x flex justify-evenly" :class="`${prefixCls}-sign-in-way`">
<a-button href="https://doc.iocoder.cn/" target="_blank" class="w-1/4">
📚开发指南
</a-button>
<a-button href="https://doc.iocoder.cn/video/" target="_blank" class="w-1/4 pl-1">
🔥视频教程
</a-button>
<a-button href="https://www.iocoder.cn/Interview/good-collection/" target="_blank" class="w-1/4 pl-1">
面试手册
</a-button>
<a-button href="http://static.yudao.iocoder.cn/mp/xinyu370.jpeg" target="_blank" class="w-1/4 pl-1">
🤝外包咨询
</a-button>
</div>
</Form>
<Verify ref="verify" mode="pop" :captcha-type="captchaType" :img-size="{ width: '360px', height: '180px' }"
@success="handleLogin" />
<Verify ref="verify" mode="pop" :captcha-type="captchaType" :img-size="{ width: '360px', height: '180px' }" @success="handleLogin" />
</template>

View File

@ -1,58 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { createFormSchema, updateFormSchema } from './applicant.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { createApplicant, getApplicant, updateApplicant } from '@/api/recruit/applicant'
defineOptions({ name: 'ApplicantModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, resetSchema, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: createFormSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
resetSchema(updateFormSchema)
const res = await getApplicant(data.record.id)
setFieldsValue({ ...res })
}
})
async function handleSubmit() {
try {
const values = await validate()
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await updateApplicant(values)
else
await createApplicant(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
} finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

View File

@ -1,91 +0,0 @@
<script lang="ts" setup>
import ApplicantModal from './ApplicantModal.vue'
import { columns, searchFormSchema } from './applicant.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { deleteApplicant, exportApplicant, getApplicantPage } from '@/api/recruit/applicant'
defineOptions({ name: 'Applicant' })
const { t } = useI18n()
const { createConfirm, createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const [registerTable, { getForm, reload }] = useTable({
title: '求职者信息列表',
api: getApplicantPage,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
// function handleEdit(record: Recordable) {
// openModal(true, { record, isUpdate: true })
// }
async function handleExport() {
createConfirm({
title: t('common.exportTitle'),
iconType: 'warning',
content: t('common.exportMessage'),
async onOk() {
await exportApplicant(getForm().getFieldsValue())
createMessage.success(t('common.exportSuccessText'))
},
})
}
async function handleDelete(record: Recordable) {
await deleteApplicant(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div>
<!-- 表格 -->
<BasicTable @register="registerTable">
<template #toolbar>
<a-button type="primary" v-auth="['recruit:applicant:create']" :preIcon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
<a-button v-auth="['recruit:applicant:export']" :preIcon="IconEnum.EXPORT" @click="handleExport">
{{ t('action.export') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction :actions="[
// { icon: IconEnum.EDIT, label: t('action.edit'), auth: 'recruit:applicant:update', onClick: handleEdit.bind(null, record) },
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: 'recruit:applicant:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]" />
</template>
</template>
</BasicTable>
<ApplicantModal @register="registerModal" @success="reload()" />
</div>
</template>