();
+ return new TaskMethodInfo
+ {
+ JobName = spareTimeAttribute.WorkerName,
+ RequestUrl = $"{m.DeclaringType.Name}/{m.Name}",
+ Cron = spareTimeAttribute.CronExpression,
+ DoOnce = spareTimeAttribute.DoOnce,
+ ExecuteType = spareTimeAttribute.ExecuteType,
+ Interval = (int)spareTimeAttribute.Interval / 1000,
+ StartNow = spareTimeAttribute.StartNow,
+ RequestType = RequestTypeEnum.Run,
+ Remark = spareTimeAttribute.Description,
+ TimerType = string.IsNullOrEmpty(spareTimeAttribute.CronExpression) ? SpareTimeTypes.Interval : SpareTimeTypes.Cron,
+ MethodName = m.Name,
+ DeclaringType = m.DeclaringType
+ };
+ }));
+
+ await _cache.SetAsync(CommonConst.CACHE_KEY_TIMER_JOB, taskMethods);
+ return taskMethods;
}
}
}
diff --git a/Api/Ewide.Core/applicationconfig.json b/Api/Ewide.Core/applicationconfig.json
index 1aaef5a..70149cf 100644
--- a/Api/Ewide.Core/applicationconfig.json
+++ b/Api/Ewide.Core/applicationconfig.json
@@ -8,6 +8,12 @@
"Title": "Admin.NET通用权限管理平台",
"Description": "前后端分离架构,开箱即用,紧随前沿技术。
后台.NET5平台基于Furion框架,前端基于XiaoNuo生态技术框架的vue版本。
Furion框架,让 .NET 开发更简单,更通用,更流行
XiaoNuo前端框架采用Vue2.x + AntDesign Vue pro1.x + Axios",
"Version": "1.0.0"
+ },
+ {
+ "Group": "HouseBusiness",
+ "Title": "城镇房屋业务接口",
+ "Description": "城镇房屋业务接口",
+ "Version": "1.0.1"
}
]
},
diff --git a/web-react/src/pages/system/timers/form.jsx b/web-react/src/pages/system/timers/form.jsx
new file mode 100644
index 0000000..1c567ea
--- /dev/null
+++ b/web-react/src/pages/system/timers/form.jsx
@@ -0,0 +1,243 @@
+import React, { Component } from 'react'
+import { Row, Col, Form, Input, Spin, Radio, Switch } from 'antd'
+import { AntIcon } from 'components'
+import { cloneDeep } from 'lodash'
+import { api } from 'common/api'
+
+const initialValues = { requestType: 2, startNow: 0, doOnce: 1, timerType: 1, executeType: 1 }
+
+export default class form extends Component {
+ state = {
+ // 加载状态
+ loading: true,
+ timerType: 1,
+ requestType: 2,
+ }
+ // 表单实例
+ form = React.createRef()
+
+ // 初始化数据
+ record = {}
+
+ /**
+ * mount后回调
+ */
+ componentDidMount() {
+ this.props.created && this.props.created(this)
+ }
+
+ /**
+ * 填充数据
+ * 可以在设置this.record之后对其作出数据结构调整
+ * [异步,必要]
+ * @param {*} params
+ */
+ async fillData(params) {
+ if (params.id) {
+ this.record = (await api.sysTimersDetail({ id: params.id })).data
+ }
+ this.record = {
+ ...this.record,
+ }
+ //#region 从后端转换成前段所需格式
+ //#endregion
+ this.form.current && this.form.current.setFieldsValue(this.record)
+
+ this.setState({
+ loading: false,
+ })
+ }
+
+ /**
+ * 获取数据
+ * 可以对postData进行数据结构调整
+ * [异步,必要]
+ * @returns
+ */
+ async getData() {
+ const form = this.form.current
+
+ const valid = await form.validateFields()
+ if (valid) {
+ const postData = form.getFieldsValue()
+ if (this.record) {
+ postData.id = this.record.id
+ }
+ //#region 从前段转换后端所需格式
+ //#endregion
+ return postData
+ }
+ }
+
+ render() {
+ const { requestType, timerType } = this.state
+ return (
+ }>
+
+
+ )
+ }
+}
diff --git a/web-react/src/pages/system/timers/index.jsx b/web-react/src/pages/system/timers/index.jsx
new file mode 100644
index 0000000..210818d
--- /dev/null
+++ b/web-react/src/pages/system/timers/index.jsx
@@ -0,0 +1,318 @@
+import React, { Component } from 'react'
+import { Button, Card, Form, Input, message as Message, Popconfirm } from 'antd'
+import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions } from 'components'
+import { api } from 'common/api'
+import auth from 'components/authorized/handler'
+import { isEqual } from 'lodash'
+import getDictData from 'util/dic'
+import { toCamelCase } from 'util/format'
+import FormBody from './form'
+
+/**
+ * 配置页面所需接口函数
+ */
+const apiAction = {
+ page: api.sysTimersPage,
+ add: api.sysTimersAdd,
+ edit: api.sysTimersEdit,
+ delete: api.sysTimersDelete,
+ start: api.sysTimersStart,
+ stop: api.sysTimersStop,
+}
+
+const name = '任务计划'
+const authName = 'sysTimers'
+export default class index extends Component {
+ state = {
+ codes: {
+ requestType: [
+ { code: '0', value: 'Run' },
+ { code: '1', value: 'Get' },
+ { code: '2', value: 'Post' },
+ { code: '3', value: 'Put' },
+ { code: '4', value: 'Delete' },
+ ],
+ timerStatus: [
+ { code: '0', value: '运行中' },
+ { code: '1', value: '已停止' },
+ { code: '2', value: '执行失败' },
+ { code: '3', value: '已取消' },
+ ],
+ },
+ }
+ // 表格实例
+ table = React.createRef()
+
+ // 新增窗口实例
+ addForm = React.createRef()
+ // 编辑窗口实例
+ editForm = React.createRef()
+
+ columns = [
+ {
+ title: '任务名称',
+ width: 80,
+ dataIndex: 'jobName',
+ },
+ {
+ title: '请求地址',
+ width: 300,
+ dataIndex: 'requestUrl',
+ },
+ {
+ title: '请求类型',
+ width: 80,
+ dataIndex: 'requestType',
+ render: text => <>{this.bindCodeValue(text, 'request_type')}>,
+ },
+ {
+ title: '请求参数',
+ width: 300,
+ dataIndex: 'requestParameters',
+ },
+ {
+ title: '间隔时间',
+ width: 80,
+ dataIndex: 'interval',
+ },
+ {
+ title: 'Cron',
+ width: 300,
+ dataIndex: 'cron',
+ },
+ {
+ title: '执行次数',
+ width: 80,
+ dataIndex: 'runNumber',
+ },
+ {
+ title: '状态',
+ width: 80,
+ dataIndex: 'timerStatus',
+ render: (text, record) => (
+ <>
+ {(text == 0 || text == 1 || text == 2) && (
+
+ text == 0
+ ? this.onStop(record.jobName)
+ : this.onStart(record.jobName)
+ }
+ >
+ {this.bindCodeValue(text, 'timer_status')}
+
+ )}
+ {text == 3 && this.bindCodeValue(text, 'timer_status')}
+ >
+ ),
+ },
+ {
+ title: '备注',
+ width: 120,
+ dataIndex: 'remark',
+ },
+ ]
+
+ /**
+ * 构造函数,在渲染前动态添加操作字段等
+ * @param {*} props
+ */
+ constructor(props) {
+ super(props)
+
+ const flag = auth({ [authName]: [['edit'], ['delete']] })
+
+ if (flag) {
+ this.columns.push({
+ title: '操作',
+ width: 150,
+ dataIndex: 'actions',
+ render: (text, { id }) => (
+
+
+ this.onOpen(this.editForm, id)}>编辑
+
+
+ this.onDelete(id)}
+ >
+ 删除
+
+
+
+ ),
+ })
+ }
+ }
+
+ /**
+ * 阻止外部组件引发的渲染,提升性能
+ * 可自行添加渲染条件
+ * [必要]
+ * @param {*} props
+ * @param {*} state
+ * @returns
+ */
+ shouldComponentUpdate(props, state) {
+ return !isEqual(this.state, state)
+ }
+
+ /**
+ * 加载字典数据,之后开始加载表格数据
+ * 如果必须要加载字典数据,可直接对表格设置autoLoad=true
+ */
+ componentDidMount() {
+ const { onLoading, onLoadData } = this.table.current
+ onLoading()
+ onLoadData()
+ // getDictData('house_company_type').then(codes => {
+ // this.setState({ codes }, () => {
+ // onLoadData()
+ // })
+ // })
+ }
+
+ /**
+ * 调用加载数据接口,可在调用前对query进行处理
+ * [异步,必要]
+ * @param {*} params
+ * @param {*} query
+ * @returns
+ */
+ loadData = async (params, query) => {
+ const { data } = await apiAction.page({
+ ...params,
+ ...query,
+ })
+ return data
+ }
+
+ /**
+ * 绑定字典数据
+ * @param {*} code
+ * @param {*} name
+ * @returns
+ */
+ bindCodeValue(code, name) {
+ name = toCamelCase(name)
+ const codes = this.state.codes[name]
+ if (codes) {
+ const c = codes.find(p => p.code == code)
+ if (c) {
+ return c.value
+ }
+ }
+ return null
+ }
+
+ /**
+ * 打开新增/编辑弹窗
+ * @param {*} modal
+ * @param {*} id
+ */
+ onOpen(modal, id) {
+ modal.current.open({ id })
+ }
+
+ /**
+ * 对表格上的操作进行统一处理
+ * [异步]
+ * @param {*} action
+ * @param {*} successMessage
+ */
+ async onAction(action, successMessage) {
+ const { onLoading, onLoaded, onReloadData } = this.table.current
+ onLoading()
+ try {
+ if (action) {
+ await action
+ }
+ if (successMessage) {
+ Message.success(successMessage)
+ }
+ onReloadData()
+ } catch {
+ onLoaded()
+ }
+ }
+
+ /**
+ * 删除
+ * @param {*} id
+ */
+ onDelete(id) {
+ this.onAction(apiAction.delete({ id }), '删除成功')
+ }
+
+ onStart(jobName) {
+ this.onAction(apiAction.start({ jobName }), '启动成功')
+ }
+ onStop(jobName) {
+ this.onAction(apiAction.stop({ jobName }), '停止成功')
+ }
+
+ render() {
+ return (
+
+
+
+
+
+
+
+
+ }
+ operator={
+
+ }
+ onClick={() => this.onOpen(this.addForm)}
+ >
+ 新增{name}
+
+
+ }
+ />
+
+
+
+ this.table.current.onReloadData()}
+ >
+
+
+
+
+
+ this.table.current.onReloadData()}
+ >
+
+
+
+
+ )
+ }
+}