Commit 583921112bb36e6a55c280dced81c4c33eb65586

Authored by xingyu
1 parent 227e11f9

fix: todo

yudao-ui-admin-vue3/src/api/infra/fileConfig/index.ts
@@ -16,7 +16,7 @@ export interface ConfigType { @@ -16,7 +16,7 @@ export interface ConfigType {
16 export interface FileConfigVO { 16 export interface FileConfigVO {
17 id: number 17 id: number
18 name: string 18 name: string
19 - storage: string 19 + storage: number
20 master: boolean 20 master: boolean
21 visible: boolean 21 visible: boolean
22 config: ConfigType 22 config: ConfigType
yudao-ui-admin-vue3/src/views/infra/fileConfig/fileConfig.data.ts
@@ -40,14 +40,14 @@ const crudSchemas = reactive<VxeCrudSchema>({ @@ -40,14 +40,14 @@ const crudSchemas = reactive<VxeCrudSchema>({
40 { 40 {
41 title: '存储器', 41 title: '存储器',
42 field: 'storage', 42 field: 'storage',
43 - dictType: DICT_TYPE.INFRA_FILE_STORAGE, // TODO @星语:不同存储器,不同表单 43 + dictType: DICT_TYPE.INFRA_FILE_STORAGE,
44 dictClass: 'number', 44 dictClass: 'number',
45 isSearch: true 45 isSearch: true
46 }, 46 },
47 { 47 {
48 title: '主配置', 48 title: '主配置',
49 field: 'master', 49 field: 'master',
50 - dictType: DICT_TYPE.INFRA_BOOLEAN_STRING, // TODO @星语:新增时,不选择主配置 50 + dictType: DICT_TYPE.INFRA_BOOLEAN_STRING,
51 dictClass: 'boolean' 51 dictClass: 'boolean'
52 }, 52 },
53 { 53 {
yudao-ui-admin-vue3/src/views/infra/fileConfig/index.vue
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 preIcon="ep:zoom-in" 9 preIcon="ep:zoom-in"
10 :title="t('action.add')" 10 :title="t('action.add')"
11 v-hasPermi="['infra:file-config:create']" 11 v-hasPermi="['infra:file-config:create']"
12 - @click="handleCreate()" 12 + @click="handleCreate(formRef)"
13 /> 13 />
14 </template> 14 </template>
15 <template #actionbtns_default="{ row }"> 15 <template #actionbtns_default="{ row }">
@@ -53,12 +53,94 @@ @@ -53,12 +53,94 @@
53 </ContentWrap> 53 </ContentWrap>
54 <XModal v-model="dialogVisible" :title="dialogTitle"> 54 <XModal v-model="dialogVisible" :title="dialogTitle">
55 <!-- 对话框(添加 / 修改) --> 55 <!-- 对话框(添加 / 修改) -->
56 - <Form 56 + <el-form
  57 + ref="formRef"
57 v-if="['create', 'update'].includes(actionType)" 58 v-if="['create', 'update'].includes(actionType)"
58 - :schema="allSchemas.formSchema" 59 + :model="form"
59 :rules="rules" 60 :rules="rules"
60 - ref="formRef"  
61 - /> 61 + label-width="120px"
  62 + >
  63 + <el-form-item label="配置名" prop="name">
  64 + <el-input v-model="form.name" placeholder="请输入配置名" />
  65 + </el-form-item>
  66 + <el-form-item label="备注" prop="remark">
  67 + <el-input v-model="form.remark" placeholder="请输入备注" />
  68 + </el-form-item>
  69 + <el-form-item label="存储器" prop="storage">
  70 + <el-select v-model="form.storage" placeholder="请选择存储器" :disabled="form.id !== 0">
  71 + <el-option
  72 + v-for="dict in getIntDictOptions(DICT_TYPE.INFRA_FILE_STORAGE)"
  73 + :key="dict.value"
  74 + :label="dict.label"
  75 + :value="dict.value"
  76 + />
  77 + </el-select>
  78 + </el-form-item>
  79 + <!-- DB -->
  80 + <!-- Local / FTP / SFTP -->
  81 + <el-form-item
  82 + v-if="form.storage >= 10 && form.storage <= 12"
  83 + label="基础路径"
  84 + prop="config.basePath"
  85 + >
  86 + <el-input v-model="form.config.basePath" placeholder="请输入基础路径" />
  87 + </el-form-item>
  88 + <el-form-item
  89 + v-if="form.storage >= 11 && form.storage <= 12"
  90 + label="主机地址"
  91 + prop="config.host"
  92 + >
  93 + <el-input v-model="form.config.host" placeholder="请输入主机地址" />
  94 + </el-form-item>
  95 + <el-form-item
  96 + v-if="form.storage >= 11 && form.storage <= 12"
  97 + label="主机端口"
  98 + prop="config.port"
  99 + >
  100 + <el-input-number :min="0" v-model="form.config.port" placeholder="请输入主机端口" />
  101 + </el-form-item>
  102 + <el-form-item
  103 + v-if="form.storage >= 11 && form.storage <= 12"
  104 + label="用户名"
  105 + prop="config.username"
  106 + >
  107 + <el-input v-model="form.config.username" placeholder="请输入密码" />
  108 + </el-form-item>
  109 + <el-form-item
  110 + v-if="form.storage >= 11 && form.storage <= 12"
  111 + label="密码"
  112 + prop="config.password"
  113 + >
  114 + <el-input v-model="form.config.password" placeholder="请输入密码" />
  115 + </el-form-item>
  116 + <el-form-item v-if="form.storage === 11" label="连接模式" prop="config.mode">
  117 + <el-radio-group v-model="form.config.mode">
  118 + <el-radio key="Active" label="Active">主动模式</el-radio>
  119 + <el-radio key="Passive" label="Passive">主动模式</el-radio>
  120 + </el-radio-group>
  121 + </el-form-item>
  122 + <!-- S3 -->
  123 + <el-form-item v-if="form.storage === 20" label="节点地址" prop="config.endpoint">
  124 + <el-input v-model="form.config.endpoint" placeholder="请输入节点地址" />
  125 + </el-form-item>
  126 + <el-form-item v-if="form.storage === 20" label="存储 bucket" prop="config.bucket">
  127 + <el-input v-model="form.config.bucket" placeholder="请输入 bucket" />
  128 + </el-form-item>
  129 + <el-form-item v-if="form.storage === 20" label="accessKey" prop="config.accessKey">
  130 + <el-input v-model="form.config.accessKey" placeholder="请输入 accessKey" />
  131 + </el-form-item>
  132 + <el-form-item v-if="form.storage === 20" label="accessSecret" prop="config.accessSecret">
  133 + <el-input v-model="form.config.accessSecret" placeholder="请输入 accessSecret" />
  134 + </el-form-item>
  135 + <!-- 通用 -->
  136 + <el-form-item v-if="form.storage === 20" label="自定义域名">
  137 + <!-- 无需参数校验,所以去掉 prop -->
  138 + <el-input v-model="form.config.domain" placeholder="请输入自定义域名" />
  139 + </el-form-item>
  140 + <el-form-item v-else-if="form.storage" label="自定义域名" prop="config.domain">
  141 + <el-input v-model="form.config.domain" placeholder="请输入自定义域名" />
  142 + </el-form-item>
  143 + </el-form>
62 <!-- 对话框(详情) --> 144 <!-- 对话框(详情) -->
63 <Descriptions 145 <Descriptions
64 v-if="actionType === 'detail'" 146 v-if="actionType === 'detail'"
@@ -73,7 +155,7 @@ @@ -73,7 +155,7 @@
73 type="primary" 155 type="primary"
74 :title="t('action.save')" 156 :title="t('action.save')"
75 :loading="actionLoading" 157 :loading="actionLoading"
76 - @click="submitForm()" 158 + @click="submitForm(formRef)"
77 /> 159 />
78 <!-- 按钮:关闭 --> 160 <!-- 按钮:关闭 -->
79 <XButton :loading="actionLoading" :title="t('dialog.close')" @click="dialogVisible = false" /> 161 <XButton :loading="actionLoading" :title="t('dialog.close')" @click="dialogVisible = false" />
@@ -82,15 +164,26 @@ @@ -82,15 +164,26 @@
82 </template> 164 </template>
83 <script setup lang="ts" name="FileConfig"> 165 <script setup lang="ts" name="FileConfig">
84 // 全局相关的 import 166 // 全局相关的 import
85 -import { ref, unref } from 'vue' 167 +import { reactive, ref } from 'vue'
  168 +import {
  169 + ElForm,
  170 + ElFormItem,
  171 + FormInstance,
  172 + ElSelect,
  173 + ElOption,
  174 + ElInput,
  175 + ElInputNumber,
  176 + ElRadio,
  177 + ElRadioGroup
  178 +} from 'element-plus'
86 import { useI18n } from '@/hooks/web/useI18n' 179 import { useI18n } from '@/hooks/web/useI18n'
87 import { useMessage } from '@/hooks/web/useMessage' 180 import { useMessage } from '@/hooks/web/useMessage'
88 import { useVxeGrid } from '@/hooks/web/useVxeGrid' 181 import { useVxeGrid } from '@/hooks/web/useVxeGrid'
89 import { VxeGridInstance } from 'vxe-table' 182 import { VxeGridInstance } from 'vxe-table'
90 -import { FormExpose } from '@/components/Form'  
91 // 业务相关的 import 183 // 业务相关的 import
92 import * as FileConfigApi from '@/api/infra/fileConfig' 184 import * as FileConfigApi from '@/api/infra/fileConfig'
93 import { rules, allSchemas } from './fileConfig.data' 185 import { rules, allSchemas } from './fileConfig.data'
  186 +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
94 187
95 const { t } = useI18n() // 国际化 188 const { t } = useI18n() // 国际化
96 const message = useMessage() // 消息弹窗 189 const message = useMessage() // 消息弹窗
@@ -107,8 +200,30 @@ const actionLoading = ref(false) // 遮罩层 @@ -107,8 +200,30 @@ const actionLoading = ref(false) // 遮罩层
107 const actionType = ref('') // 操作按钮的类型 200 const actionType = ref('') // 操作按钮的类型
108 const dialogVisible = ref(false) // 是否显示弹出层 201 const dialogVisible = ref(false) // 是否显示弹出层
109 const dialogTitle = ref('edit') // 弹出层标题 202 const dialogTitle = ref('edit') // 弹出层标题
110 -const formRef = ref<FormExpose>() // 表单 Ref 203 +const formRef = ref<FormInstance>() // 表单 Ref
111 const detailData = ref() // 详情 Ref 204 const detailData = ref() // 详情 Ref
  205 +let form = reactive<FileConfigApi.FileConfigVO>({
  206 + id: 0,
  207 + name: '',
  208 + storage: 0,
  209 + master: false,
  210 + visible: false,
  211 + config: {
  212 + basePath: '',
  213 + host: '',
  214 + port: '',
  215 + username: '',
  216 + password: '',
  217 + mode: '',
  218 + endpoint: '',
  219 + bucket: '',
  220 + accessKey: '',
  221 + accessSecret: '',
  222 + domain: ''
  223 + },
  224 + remark: '',
  225 + createTime: ''
  226 +})
112 // 设置标题 227 // 设置标题
113 const setDialogTile = (type: string) => { 228 const setDialogTile = (type: string) => {
114 dialogTitle.value = t('action.' + type) 229 dialogTitle.value = t('action.' + type)
@@ -117,16 +232,17 @@ const setDialogTile = (type: string) =&gt; { @@ -117,16 +232,17 @@ const setDialogTile = (type: string) =&gt; {
117 } 232 }
118 233
119 // 新增操作 234 // 新增操作
120 -const handleCreate = () => { 235 +const handleCreate = (formEl: FormInstance | undefined) => {
121 setDialogTile('create') 236 setDialogTile('create')
  237 + formEl?.resetFields()
122 } 238 }
123 239
124 // 修改操作 240 // 修改操作
125 const handleUpdate = async (rowId: number) => { 241 const handleUpdate = async (rowId: number) => {
126 - setDialogTile('update')  
127 // 设置数据 242 // 设置数据
128 const res = await FileConfigApi.getFileConfigApi(rowId) 243 const res = await FileConfigApi.getFileConfigApi(rowId)
129 - unref(formRef)?.setValues(res) 244 + form = res
  245 + setDialogTile('update')
130 } 246 }
131 247
132 // 详情操作 248 // 详情操作
@@ -153,20 +269,18 @@ const handleDelete = async (rowId: number) =&gt; { @@ -153,20 +269,18 @@ const handleDelete = async (rowId: number) =&gt; {
153 } 269 }
154 270
155 // 提交按钮 271 // 提交按钮
156 -const submitForm = async () => {  
157 - const elForm = unref(formRef)?.getElFormRef()  
158 - if (!elForm) return  
159 - elForm.validate(async (valid) => { 272 +const submitForm = async (formEl: FormInstance | undefined) => {
  273 + if (!formEl) return
  274 + formEl.validate(async (valid) => {
160 if (valid) { 275 if (valid) {
161 actionLoading.value = true 276 actionLoading.value = true
162 // 提交请求 277 // 提交请求
163 try { 278 try {
164 - const data = unref(formRef)?.formModel as FileConfigApi.FileConfigVO  
165 if (actionType.value === 'create') { 279 if (actionType.value === 'create') {
166 - await FileConfigApi.createFileConfigApi(data) 280 + await FileConfigApi.createFileConfigApi(form)
167 message.success(t('common.createSuccess')) 281 message.success(t('common.createSuccess'))
168 } else { 282 } else {
169 - await FileConfigApi.updateFileConfigApi(data) 283 + await FileConfigApi.updateFileConfigApi(form)
170 message.success(t('common.updateSuccess')) 284 message.success(t('common.updateSuccess'))
171 } 285 }
172 dialogVisible.value = false 286 dialogVisible.value = false
yudao-ui-admin-vue3/src/views/infra/job/index.vue
@@ -100,7 +100,9 @@ @@ -100,7 +100,9 @@
100 <template #monitorTimeout="{ row }"> 100 <template #monitorTimeout="{ row }">
101 <span>{{ row.monitorTimeout > 0 ? row.monitorTimeout + ' 毫秒' : '未开启' }}</span> 101 <span>{{ row.monitorTimeout > 0 ? row.monitorTimeout + ' 毫秒' : '未开启' }}</span>
102 </template> 102 </template>
103 - <!-- TODO @星语:有个【后续执行时间】字段:2022-11-26 23:03:16; 2022-11-26 23:03:17; 2022-11-26 23:03:18; 2022-11-26 23:03:19; 2022-11-26 23:03:20 --> 103 + <template #nextTimes>
  104 + <span>{{ Array.from(nextTimes, (x) => parseTime(x)).join('; ') }}</span>
  105 + </template>
104 </Descriptions> 106 </Descriptions>
105 <!-- 操作按钮 --> 107 <!-- 操作按钮 -->
106 <template #footer> 108 <template #footer>
@@ -151,6 +153,7 @@ const dialogTitle = ref(&#39;edit&#39;) // 弹出层标题 @@ -151,6 +153,7 @@ const dialogTitle = ref(&#39;edit&#39;) // 弹出层标题
151 const formRef = ref<FormExpose>() // 表单 Ref 153 const formRef = ref<FormExpose>() // 表单 Ref
152 const detailRef = ref() // 详情 Ref 154 const detailRef = ref() // 详情 Ref
153 const cronExpression = ref('') 155 const cronExpression = ref('')
  156 +const nextTimes = ref([])
154 const shortcuts = ref([ 157 const shortcuts = ref([
155 { 158 {
156 text: '每天8点和12点 (自定义追加)', 159 text: '每天8点和12点 (自定义追加)',
@@ -169,10 +172,12 @@ const handleCreate = () =&gt; { @@ -169,10 +172,12 @@ const handleCreate = () =&gt; {
169 cronExpression.value = '' 172 cronExpression.value = ''
170 setDialogTile('create') 173 setDialogTile('create')
171 } 174 }
  175 +
172 // 导出操作 176 // 导出操作
173 const handleExport = async () => { 177 const handleExport = async () => {
174 await exportList(xGrid, '定时任务.xls') 178 await exportList(xGrid, '定时任务.xls')
175 } 179 }
  180 +
176 // 修改操作 181 // 修改操作
177 const handleUpdate = async (rowId: number) => { 182 const handleUpdate = async (rowId: number) => {
178 setDialogTile('update') 183 setDialogTile('update')
@@ -187,8 +192,57 @@ const handleDetail = async (rowId: number) =&gt; { @@ -187,8 +192,57 @@ const handleDetail = async (rowId: number) =&gt; {
187 // 设置数据 192 // 设置数据
188 const res = await JobApi.getJobApi(rowId) 193 const res = await JobApi.getJobApi(rowId)
189 detailRef.value = res 194 detailRef.value = res
  195 + // 后续执行时长
  196 + const jobNextTime = await JobApi.getJobNextTimesApi(rowId)
  197 + nextTimes.value = jobNextTime
190 setDialogTile('detail') 198 setDialogTile('detail')
191 } 199 }
  200 +
  201 +const parseTime = (time) => {
  202 + if (!time) {
  203 + return null
  204 + }
  205 + const format = '{y}-{m}-{d} {h}:{i}:{s}'
  206 + let date
  207 + if (typeof time === 'object') {
  208 + date = time
  209 + } else {
  210 + if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
  211 + time = parseInt(time)
  212 + } else if (typeof time === 'string') {
  213 + time = time
  214 + .replace(new RegExp(/-/gm), '/')
  215 + .replace('T', ' ')
  216 + .replace(new RegExp(/\.[\d]{3}/gm), '')
  217 + }
  218 + if (typeof time === 'number' && time.toString().length === 10) {
  219 + time = time * 1000
  220 + }
  221 + date = new Date(time)
  222 + }
  223 + const formatObj = {
  224 + y: date.getFullYear(),
  225 + m: date.getMonth() + 1,
  226 + d: date.getDate(),
  227 + h: date.getHours(),
  228 + i: date.getMinutes(),
  229 + s: date.getSeconds(),
  230 + a: date.getDay()
  231 + }
  232 + const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
  233 + let value = formatObj[key]
  234 + // Note: getDay() returns 0 on Sunday
  235 + if (key === 'a') {
  236 + return ['日', '一', '二', '三', '四', '五', '六'][value]
  237 + }
  238 + if (result.length > 0 && value < 10) {
  239 + value = '0' + value
  240 + }
  241 + return value || 0
  242 + })
  243 + return time_str
  244 +}
  245 +
192 // 删除操作 246 // 删除操作
193 const handleDelete = async (rowId: number) => { 247 const handleDelete = async (rowId: number) => {
194 await deleteData(xGrid, rowId) 248 await deleteData(xGrid, rowId)
yudao-ui-admin-vue3/src/views/infra/job/job.data.ts
@@ -49,6 +49,12 @@ const crudSchemas = reactive&lt;VxeCrudSchema&gt;({ @@ -49,6 +49,12 @@ const crudSchemas = reactive&lt;VxeCrudSchema&gt;({
49 field: 'cronExpression' 49 field: 'cronExpression'
50 }, 50 },
51 { 51 {
  52 + title: '后续执行时间',
  53 + field: 'nextTimes',
  54 + isTable: false,
  55 + isForm: false
  56 + },
  57 + {
52 title: '重试次数', 58 title: '重试次数',
53 field: 'retryCount', 59 field: 'retryCount',
54 isTable: false 60 isTable: false