From 113f17cd18dc98b4c5d7e4817aeedb85fd423b70 Mon Sep 17 00:00:00 2001
From: xingyu <xingyu4j@vip.qq.com>
Date: Tue, 21 Mar 2023 23:11:45 +0800
Subject: [PATCH] feat: views

---
 src/components/Modal/src/props.ts             |   2 +-
 src/components/Table/src/hooks/useRender.ts   |   5 +
 src/utils/http/axios/Axios.ts                 |   6 +-
 src/views/system/dict/DictDataModel.vue       |   2 +-
 src/views/system/dict/DictTypeModel.vue       |   2 +-
 .../system/oauth2/client/ClientModel.vue      |  58 +++++
 src/views/system/oauth2/client/client.data.ts | 207 ++++++++++++++++++
 src/views/system/oauth2/client/index.vue      |  80 ++++++-
 .../system/operatelog/operateLog.data.ts      |   2 +-
 9 files changed, 356 insertions(+), 8 deletions(-)
 create mode 100644 src/views/system/oauth2/client/ClientModel.vue
 create mode 100644 src/views/system/oauth2/client/client.data.ts

diff --git a/src/components/Modal/src/props.ts b/src/components/Modal/src/props.ts
index fbfec2b2..92e207fc 100644
--- a/src/components/Modal/src/props.ts
+++ b/src/components/Modal/src/props.ts
@@ -75,7 +75,7 @@ export const basicProps = Object.assign({}, modalProps, {
 
   visible: { type: Boolean },
 
-  width: [String, Number] as PropType<string | number>,
+  width: { type: [String, Number] as PropType<string | number>, default: '40%' },
 
   wrapClassName: { type: String },
 
diff --git a/src/components/Table/src/hooks/useRender.ts b/src/components/Table/src/hooks/useRender.ts
index 11110fd6..b9f46d61 100644
--- a/src/components/Table/src/hooks/useRender.ts
+++ b/src/components/Table/src/hooks/useRender.ts
@@ -14,6 +14,11 @@ export const useRender = {
       })
     }
   },
+  renderText: (text, val) => {
+    if (text) {
+      return text + ' ' + val
+    }
+  },
   renderTag: (text, color?) => {
     if (!color) {
       return h(Tag, { color }, () => text)
diff --git a/src/utils/http/axios/Axios.ts b/src/utils/http/axios/Axios.ts
index e42fa204..23693c8c 100644
--- a/src/utils/http/axios/Axios.ts
+++ b/src/utils/http/axios/Axios.ts
@@ -125,9 +125,9 @@ export class VAxios {
               requestList = []
               // TODO
               console.info('刷新令牌end', res)
-              return new Promise((resolve) => {
-                resolve(this.axiosInstance(config))
-              })
+              // return new Promise((resolve) => {
+              //   resolve(this.axiosInstance(config))
+              // })
               // res = await Promise.all([this.axiosInstance(config)])[0]
             } catch (e) {
               console.info(e)
diff --git a/src/views/system/dict/DictDataModel.vue b/src/views/system/dict/DictDataModel.vue
index 728146bb..d4bdfbb7 100644
--- a/src/views/system/dict/DictDataModel.vue
+++ b/src/views/system/dict/DictDataModel.vue
@@ -3,7 +3,7 @@
     <BasicForm @register="registerForm" />
   </BasicModal>
 </template>
-<script lang="ts" setup name="PostModal">
+<script lang="ts" setup name="DictDataModal">
 import { ref, computed, unref } from 'vue'
 import { BasicModal, useModalInner } from '@/components/Modal'
 import { BasicForm, useForm } from '@/components/Form'
diff --git a/src/views/system/dict/DictTypeModel.vue b/src/views/system/dict/DictTypeModel.vue
index 0d7c849e..2b395af1 100644
--- a/src/views/system/dict/DictTypeModel.vue
+++ b/src/views/system/dict/DictTypeModel.vue
@@ -3,7 +3,7 @@
     <BasicForm @register="registerForm" />
   </BasicModal>
 </template>
-<script lang="ts" setup name="PostModal">
+<script lang="ts" setup name="DictTypeModal">
 import { ref, computed, unref } from 'vue'
 import { BasicModal, useModalInner } from '@/components/Modal'
 import { BasicForm, useForm } from '@/components/Form'
diff --git a/src/views/system/oauth2/client/ClientModel.vue b/src/views/system/oauth2/client/ClientModel.vue
new file mode 100644
index 00000000..e2214c5d
--- /dev/null
+++ b/src/views/system/oauth2/client/ClientModel.vue
@@ -0,0 +1,58 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
+    <BasicForm @register="registerForm" />
+  </BasicModal>
+</template>
+<script lang="ts" setup name="ClientModel">
+import { ref, computed, unref } from 'vue'
+import { BasicModal, useModalInner } from '@/components/Modal'
+import { BasicForm, useForm } from '@/components/Form'
+import { formSchema } from './client.data'
+import { createOAuth2ClientApi, getOAuth2ClientApi, updateOAuth2ClientApi } from '@/api/system/oauth2/client'
+
+const emit = defineEmits(['success', 'register'])
+const isUpdate = ref(true)
+const rowId = ref()
+
+const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
+  labelWidth: 160,
+  baseColProps: { span: 24 },
+  schemas: formSchema,
+  showActionButtonGroup: false,
+  actionColOptions: {
+    span: 23
+  }
+})
+
+const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
+  resetFields()
+  setModalProps({ confirmLoading: false })
+  isUpdate.value = !!data?.isUpdate
+
+  if (unref(isUpdate)) {
+    const res = await getOAuth2ClientApi(data.record.id)
+    rowId.value = res.id
+    setFieldsValue({
+      ...res
+    })
+  }
+})
+
+const getTitle = computed(() => (!unref(isUpdate) ? '新增应用' : '编辑应用'))
+
+async function handleSubmit() {
+  try {
+    const values = await validate()
+    setModalProps({ confirmLoading: true })
+    if (unref(isUpdate)) {
+      await updateOAuth2ClientApi(values)
+    } else {
+      await createOAuth2ClientApi(values)
+    }
+    closeModal()
+    emit('success')
+  } finally {
+    setModalProps({ confirmLoading: false })
+  }
+}
+</script>
diff --git a/src/views/system/oauth2/client/client.data.ts b/src/views/system/oauth2/client/client.data.ts
new file mode 100644
index 00000000..1f8a8d39
--- /dev/null
+++ b/src/views/system/oauth2/client/client.data.ts
@@ -0,0 +1,207 @@
+import { BasicColumn, FormSchema, useRender } from '@/components/Table'
+import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
+
+export const columns: BasicColumn[] = [
+  {
+    title: '客户端编号',
+    dataIndex: 'clientId',
+    width: 200
+  },
+  {
+    title: '客户端密钥',
+    dataIndex: 'secret',
+    width: 100
+  },
+  {
+    title: '应用名',
+    dataIndex: 'name',
+    width: 100
+  },
+  {
+    title: '应用图标',
+    dataIndex: 'logo',
+    width: 120,
+    customRender: ({ text }) => {
+      return useRender.renderImg(text)
+    }
+  },
+  {
+    title: '状态',
+    dataIndex: 'status',
+    width: 180,
+    customRender: ({ text }) => {
+      return useRender.renderDict(text, DICT_TYPE.COMMON_STATUS)
+    }
+  },
+  {
+    title: '访问令牌的有效期',
+    dataIndex: 'accessTokenValiditySeconds',
+    width: 100,
+    customRender: ({ text }) => {
+      return useRender.renderText(text, '秒')
+    }
+  },
+  {
+    title: '刷新令牌的有效期',
+    dataIndex: 'refreshTokenValiditySeconds',
+    width: 100,
+    customRender: ({ text }) => {
+      return useRender.renderText(text, '秒')
+    }
+  },
+  {
+    title: '授权类型',
+    dataIndex: 'authorizedGrantTypes',
+    width: 180,
+    customRender: ({ text }) => {
+      return useRender.renderTags(text)
+    }
+  },
+  {
+    title: '创建时间',
+    dataIndex: 'createTime',
+    width: 180,
+    customRender: ({ text }) => {
+      return useRender.renderDate(text)
+    }
+  }
+]
+
+export const searchFormSchema: FormSchema[] = [
+  {
+    label: '应用名',
+    field: 'name',
+    component: 'Input',
+    colProps: { span: 8 }
+  },
+  {
+    label: '状态',
+    field: 'status',
+    component: 'Select',
+    componentProps: {
+      options: getIntDictOptions(DICT_TYPE.COMMON_STATUS)
+    },
+    colProps: { span: 8 }
+  }
+]
+
+export const formSchema: FormSchema[] = [
+  {
+    label: '编号',
+    field: 'id',
+    show: false,
+    component: 'Input'
+  },
+  {
+    label: '客户端编号',
+    field: 'secret',
+    required: true,
+    component: 'Input'
+  },
+  {
+    label: '客户端密钥',
+    field: 'secret',
+    required: true,
+    component: 'Input'
+  },
+  {
+    label: '应用名',
+    field: 'name',
+    required: true,
+    component: 'Input'
+  },
+  // TODO UPLOAD
+  {
+    label: '应用图标',
+    field: 'logo',
+    required: true,
+    component: 'Input'
+  },
+  {
+    label: '应用描述',
+    field: 'description',
+    component: 'InputTextArea'
+  },
+  {
+    label: '状态',
+    field: 'status',
+    component: 'Select',
+    defaultValue: 0,
+    componentProps: {
+      options: getIntDictOptions(DICT_TYPE.COMMON_STATUS)
+    }
+  },
+  {
+    label: '访问令牌的有效期',
+    field: 'accessTokenValiditySeconds',
+    required: true,
+    component: 'InputNumber'
+  },
+  {
+    label: '刷新令牌的有效期',
+    field: 'refreshTokenValiditySeconds',
+    required: true,
+    component: 'InputNumber'
+  },
+  {
+    label: '授权类型',
+    field: 'authorizedGrantTypes',
+    required: true,
+    component: 'Select',
+    componentProps: {
+      options: getStrDictOptions(DICT_TYPE.SYSTEM_OAUTH2_GRANT_TYPE),
+      mode: 'multiple'
+    }
+  },
+  {
+    label: '授权范围',
+    field: 'scopes',
+    component: 'Select',
+    componentProps: {
+      mode: 'tags',
+      options: []
+    }
+  },
+  {
+    label: '自动授权范围',
+    field: 'autoApproveScopes',
+    component: 'Select',
+    componentProps: {
+      mode: 'tags',
+      options: []
+    }
+  },
+  {
+    label: '可重定向的 URI 地址',
+    field: 'redirectUris',
+    required: true,
+    component: 'Select',
+    componentProps: {
+      mode: 'tags',
+      options: []
+    }
+  },
+  {
+    label: '权限',
+    field: 'authorities',
+    component: 'Select',
+    componentProps: {
+      mode: 'tags',
+      options: []
+    }
+  },
+  {
+    label: '资源',
+    field: 'resourceIds',
+    component: 'Select',
+    componentProps: {
+      mode: 'tags',
+      options: []
+    }
+  },
+  {
+    label: '附加信息',
+    field: 'additionalInformation',
+    component: 'InputTextArea'
+  }
+]
diff --git a/src/views/system/oauth2/client/index.vue b/src/views/system/oauth2/client/index.vue
index 3b64cfc4..b1659b75 100644
--- a/src/views/system/oauth2/client/index.vue
+++ b/src/views/system/oauth2/client/index.vue
@@ -1,3 +1,81 @@
 <template>
-  <div>开发中</div>
+  <div>
+    <BasicTable @register="registerTable">
+      <template #toolbar>
+        <a-button type="primary" @click="handleCreate"> 新增 </a-button>
+      </template>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'action'">
+          <TableAction
+            :actions="[
+              {
+                icon: 'clarity:note-edit-line',
+                label: '修改',
+                onClick: handleEdit.bind(null, record)
+              },
+              {
+                icon: 'ant-design:delete-outlined',
+                color: 'error',
+                label: '删除',
+                popConfirm: {
+                  title: '是否确认删除',
+                  placement: 'left',
+                  confirm: handleDelete.bind(null, record)
+                }
+              }
+            ]"
+          />
+        </template>
+      </template>
+    </BasicTable>
+    <ClientModel @register="registerModal" @success="reload()" />
+  </div>
 </template>
+<script lang="ts" setup name="Client">
+import { BasicTable, useTable, TableAction } from '@/components/Table'
+import { deleteOAuth2ClientApi, getOAuth2ClientPageApi } from '@/api/system/oauth2/client'
+import { useModal } from '@/components/Modal'
+import ClientModel from './ClientModel.vue'
+import { columns, searchFormSchema } from './client.data'
+import { useMessage } from '@/hooks/web/useMessage'
+
+const { createMessage } = useMessage()
+const [registerModal, { openModal }] = useModal()
+const [registerTable, { reload }] = useTable({
+  title: '应用列表',
+  api: getOAuth2ClientPageApi,
+  columns,
+  formConfig: {
+    labelWidth: 120,
+    schemas: searchFormSchema
+  },
+  useSearchForm: true,
+  showTableSetting: true,
+  showIndexColumn: false,
+  actionColumn: {
+    width: 160,
+    title: '操作',
+    dataIndex: 'action',
+    fixed: 'right'
+  }
+})
+
+function handleCreate() {
+  openModal(true, {
+    isUpdate: false
+  })
+}
+
+function handleEdit(record: Recordable) {
+  openModal(true, {
+    record,
+    isUpdate: true
+  })
+}
+
+async function handleDelete(record: Recordable) {
+  await deleteOAuth2ClientApi(record.id)
+  createMessage.success('删除成功')
+  reload()
+}
+</script>
diff --git a/src/views/system/operatelog/operateLog.data.ts b/src/views/system/operatelog/operateLog.data.ts
index 9a0b7051..ea2c32ef 100644
--- a/src/views/system/operatelog/operateLog.data.ts
+++ b/src/views/system/operatelog/operateLog.data.ts
@@ -58,7 +58,7 @@ export const columns: BasicColumn[] = [
     dataIndex: 'duration',
     width: 180,
     customRender: ({ text }) => {
-      return h('span', text + 'ms')
+      return useRender.renderText(text, 'ms')
     }
   }
 ]