diff --git a/package.json b/package.json index 7e7c2c87d..f1c95b5be 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,8 @@ "vue": "^3.2.37", "vue-filepond": "^7.0.3", "vue-grid-layout": "3.0.0-beta1", - "vue-router": "^4.1.2" + "vue-router": "^4.1.2", + "yaml": "^2.1.1" }, "devDependencies": { "@changesets/cli": "^2.23.2", diff --git a/packages/components/package.json b/packages/components/package.json index 3a5f0e473..50ce2e15c 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -49,7 +49,7 @@ "@rollup/plugin-typescript": "^8.3.3", "histoire": "^0.7.9", "unplugin-icons": "^0.14.7", - "vite-plugin-dts": "^1.3.1" + "vite-plugin-dts": "^1.4.0" }, "peerDependencies": { "vue": "^3.2.37", @@ -64,6 +64,8 @@ }, "dependencies": { "@codemirror/commands": "^6.0.1", + "@codemirror/language": "^6.2.1", + "@codemirror/legacy-modes": "^6.1.0", "@codemirror/state": "^6.1.0", "@codemirror/view": "^6.1.0", "codemirror": "^6.0.1" diff --git a/packages/components/src/components.ts b/packages/components/src/components.ts index 64d00b3fd..4f8d94a3b 100644 --- a/packages/components/src/components.ts +++ b/packages/components/src/components.ts @@ -15,3 +15,4 @@ export * from "./components/textarea"; export * from "./components/switch"; export * from "./components/dialog"; export * from "./components/pagination"; +export * from "./components/codemirror"; diff --git a/packages/components/src/components/codemirror/Codemirror.story.vue b/packages/components/src/components/codemirror/Codemirror.story.vue index 9e23afc95..b21488d70 100644 --- a/packages/components/src/components/codemirror/Codemirror.story.vue +++ b/packages/components/src/components/codemirror/Codemirror.story.vue @@ -10,7 +10,7 @@ function initState() { diff --git a/packages/components/src/components/codemirror/Codemirror.vue b/packages/components/src/components/codemirror/Codemirror.vue index 537505204..f9cc7108c 100644 --- a/packages/components/src/components/codemirror/Codemirror.vue +++ b/packages/components/src/components/codemirror/Codemirror.vue @@ -5,6 +5,12 @@ import type { EditorStateConfig } from "@codemirror/state"; import { EditorState } from "@codemirror/state"; import { EditorView } from "@codemirror/view"; import { basicSetup } from "codemirror"; +import { StreamLanguage } from "@codemirror/language"; +import { yaml } from "@codemirror/legacy-modes/mode/yaml"; + +const languages = { + yaml: StreamLanguage.define(yaml), +}; const props = defineProps({ modelValue: { @@ -15,6 +21,10 @@ const props = defineProps({ type: String, default: "auto", }, + language: { + type: String as PropType<"yaml">, + default: "yaml", + }, extensions: { type: Array as PropType, default: () => [], @@ -41,6 +51,7 @@ const createCmEditor = () => { basicSetup, EditorView.lineWrapping, customTheme, + languages[props.language], EditorView.updateListener.of((viewUpdate) => { if (viewUpdate.docChanged) { const doc = viewUpdate.state.doc.toString(); diff --git a/packages/components/src/icons/icons.ts b/packages/components/src/icons/icons.ts index 59abd6ea4..fd5e4451b 100644 --- a/packages/components/src/icons/icons.ts +++ b/packages/components/src/icons/icons.ts @@ -39,6 +39,7 @@ import IconShieldUser from "~icons/ri/shield-user-line"; import IconGitBranch from "~icons/ri/git-branch-line"; import IconStopCircle from "~icons/ri/stop-circle-line"; import IconForbidLine from "~icons/ri/forbid-line"; +import IconCodeBoxLine from "~icons/ri/code-box-line"; export { IconDashboard, @@ -82,4 +83,5 @@ export { IconGitBranch, IconStopCircle, IconForbidLine, + IconCodeBoxLine, }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f793d7600..e182751fb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,6 +71,7 @@ importers: vue-grid-layout: 3.0.0-beta1 vue-router: ^4.1.2 vue-tsc: ^0.38.8 + yaml: ^2.1.1 dependencies: '@formkit/addons': 1.0.0-beta.9_vue@3.2.37 '@formkit/core': 1.0.0-beta.9 @@ -97,6 +98,7 @@ importers: vue-filepond: 7.0.3_filepond@4.30.4+vue@3.2.37 vue-grid-layout: 3.0.0-beta1 vue-router: 4.1.2_vue@3.2.37 + yaml: 2.1.1 devDependencies: '@changesets/cli': 2.23.2 '@rushstack/eslint-patch': 1.1.4 @@ -144,6 +146,8 @@ importers: packages/components: specifiers: '@codemirror/commands': ^6.0.1 + '@codemirror/language': ^6.2.1 + '@codemirror/legacy-modes': ^6.1.0 '@codemirror/state': ^6.1.0 '@codemirror/view': ^6.1.0 '@iconify-json/ri': ^1.1.3 @@ -151,9 +155,11 @@ importers: codemirror: ^6.0.1 histoire: ^0.7.9 unplugin-icons: ^0.14.7 - vite-plugin-dts: ^1.3.1 + vite-plugin-dts: ^1.4.0 dependencies: '@codemirror/commands': 6.0.1 + '@codemirror/language': 6.2.1 + '@codemirror/legacy-modes': 6.1.0 '@codemirror/state': 6.1.0 '@codemirror/view': 6.1.0 codemirror: 6.0.1 @@ -162,7 +168,7 @@ importers: '@rollup/plugin-typescript': 8.3.3 histoire: 0.7.9 unplugin-icons: 0.14.7 - vite-plugin-dts: 1.3.1 + vite-plugin-dts: 1.4.0 packages/shared: specifiers: @@ -1644,6 +1650,12 @@ packages: style-mod: 4.0.0 dev: false + /@codemirror/legacy-modes/6.1.0: + resolution: {integrity: sha512-V/PgGpndkZeTn3Hdlg/gd8MLFdyvTCIX+iwJzjUw5iNziWiNsAY8X0jvf7m3gSfxnKkNzmid6l0g4rYSpiDaCw==} + dependencies: + '@codemirror/language': 6.2.1 + dev: false + /@codemirror/lint/6.0.0: resolution: {integrity: sha512-nUUXcJW1Xp54kNs+a1ToPLK8MadO0rMTnJB8Zk4Z8gBdrN0kqV7uvUraU/T2yqg+grDNR38Vmy/MrhQN/RgwiA==} dependencies: @@ -1652,8 +1664,8 @@ packages: crelt: 1.0.5 dev: false - /@codemirror/search/6.0.0: - resolution: {integrity: sha512-rL0rd3AhI0TAsaJPUaEwC63KHLO7KL0Z/dYozXj6E7L3wNHRyx7RfE0/j5HsIf912EE5n2PCb4Vg0rGYmDv4UQ==} + /@codemirror/search/6.0.1: + resolution: {integrity: sha512-uOinkOrM+daMduCgMPomDfKLr7drGHB4jHl3Vq6xY2WRlL7MkNsBE0b+XHYa/Mee2npsJOgwvkW4n1lMFeBW2Q==} dependencies: '@codemirror/state': 6.1.0 '@codemirror/view': 6.1.0 @@ -3659,7 +3671,7 @@ packages: '@codemirror/commands': 6.0.1 '@codemirror/language': 6.2.1 '@codemirror/lint': 6.0.0 - '@codemirror/search': 6.0.0 + '@codemirror/search': 6.0.1 '@codemirror/state': 6.1.0 '@codemirror/view': 6.1.0 dev: false @@ -7923,6 +7935,23 @@ packages: - supports-color dev: true + /vite-plugin-dts/1.4.0: + resolution: {integrity: sha512-RyDCjQzVxUeDqF+Rl1hQT+t/rKmvfvo04gaGV/l3597FpeIWGKtNF1S4x509Kx1AfHOjLa1JdmjVgnSEIv+lpw==} + engines: {node: '>=12.0.0'} + peerDependencies: + vite: '>=2.4.4' + dependencies: + '@microsoft/api-extractor': 7.23.2 + '@rushstack/node-core-library': 3.45.5 + chalk: 4.1.2 + debug: 4.3.4 + fast-glob: 3.2.11 + fs-extra: 10.1.0 + ts-morph: 14.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /vite-plugin-externals/0.5.1_vite@2.9.14: resolution: {integrity: sha512-HvRFG5y9wXoJUG9FSbSp9ikOiJRh7EzN6tJC5oIOcEj+19GUw9Z1NNCPFtAmX75Ajcr10FdELKNmuXS3lExkcg==} peerDependencies: @@ -8512,6 +8541,11 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} + /yaml/2.1.1: + resolution: {integrity: sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==} + engines: {node: '>= 14'} + dev: false + /yargs-parser/18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} diff --git a/src/modules/system/users/components/UserEditingModal.vue b/src/modules/system/users/components/UserEditingModal.vue index ce5497a5a..b6eef43cd 100644 --- a/src/modules/system/users/components/UserEditingModal.vue +++ b/src/modules/system/users/components/UserEditingModal.vue @@ -3,10 +3,18 @@ import type { PropType } from "vue"; import { computed, onMounted, ref, watch } from "vue"; import { apiClient } from "@halo-dev/admin-shared"; import type { Role, User } from "@halo-dev/api-client"; -import { IconSave, VButton, VModal } from "@halo-dev/components"; +import { + IconCodeBoxLine, + IconEye, + IconSave, + VButton, + VCodemirror, + VModal, +} from "@halo-dev/components"; import { v4 as uuid } from "uuid"; import { roleLabels } from "@/constants/labels"; import { rbacAnnotations } from "@/constants/annotations"; +import YAML from "yaml"; const props = defineProps({ visible: { @@ -24,6 +32,8 @@ const emit = defineEmits(["update:visible", "close"]); interface EditingFormState { user: User; saving: boolean; + rawMode: boolean; + raw: string; } const roles = ref([]); @@ -46,6 +56,8 @@ const editingFormState = ref({ }, }, saving: false, + rawMode: false, + raw: "", }); const selectedRole = ref(""); @@ -57,6 +69,10 @@ const creationModalTitle = computed(() => { return isUpdateMode.value ? "编辑用户" : "新增用户"; }); +const modalWidth = computed(() => { + return editingFormState.value.rawMode ? 800 : 700; +}); + const basicRoles = computed(() => { return roles.value.filter( (role) => role.metadata?.labels?.[roleLabels.TEMPLATE] !== "true" @@ -118,38 +134,62 @@ const handleCreateUser = async () => { } }; +const handleRawModeChange = () => { + editingFormState.value.rawMode = !editingFormState.value.rawMode; + + if (editingFormState.value.rawMode) { + editingFormState.value.raw = YAML.stringify(editingFormState.value.user); + } else { + editingFormState.value.user = YAML.parse(editingFormState.value.raw); + } +}; onMounted(handleFetchRoles); + + + +
+ + + + + - - - - + label="角色" + type="select" + validation="required" + > + + + + +