Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add ma-key-value component #538

Merged
merged 1 commit into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions web/src/components/ma-key-value/components/form.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<!--
- MineAdmin is committed to providing solutions for quickly building web applications
- Please view the LICENSE file that was distributed with this source code,
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<[email protected]>
- @Link https://github.com/mineadmin
-->
<script setup lang="ts">
import { VAceEditor } from "vue3-ace-editor";
import 'ace-builds/src-noconflict/mode-json'
import 'ace-builds/src-noconflict/theme-dawn'
import 'ace-builds/src-noconflict/theme-github_dark'
import { useColorMode } from '@vueuse/core'
import formatJson from '../utils/formatJson.ts'

defineOptions({ name: 'system:group:form' })

const color = useColorMode()
const t = useTrans().globalTrans
const convertArray = ref()
const content = ref();

// Watch for changes in content and format it
watch(content, (newValue) => {
try {
const parsedJson = JSON.parse(newValue);
content.value = formatJson(parsedJson); // Format the JSON
} catch (error) {
// Handle invalid JSON format if necessary
}
});
Comment on lines +26 to +33
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

watch函数中的错误处理需要完善

当前的错误处理是空的,这可能会导致用户无法得知输入的JSON格式是否有误。

建议修改为:

 watch(content, (newValue) => {
   try {
     const parsedJson = JSON.parse(newValue);
     content.value = formatJson(parsedJson);
   } catch (error) {
-    // Handle invalid JSON format if necessary
+    console.warn('JSON格式无效:', error);
+    // 可以通过UI提示用户输入的JSON格式有误
+    ElMessage.warning('请输入有效的JSON格式');
   }
 });

Committable suggestion skipped: line range outside the PR's diff.


const theme = computed(() => color.value === 'dark' ? 'github_dark' : 'dawn')

// ok事件
function add(): Promise<any> {
return new Promise((resolve, reject) => {
try {
// 将 JSON 字符串转换为数组
const parsedArray = JSON.parse(content.value)

// 如果转换成功,并且是数组,则返回成功的响应
if (Array.isArray(parsedArray)) {
resolve({
code: 200,
success: true,
message: 'success',
data: parsedArray,
})
convertArray.value = parsedArray
} else {
reject({
code: 404,
success: false,
message: 'Parsed content is not an array',
})
}
} catch (error) {
reject({
code: 404,
success: false,
message: 'Invalid JSON format',
})
}
})
}
Comment on lines +38 to +68
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

建议优化add方法的实现

当前实现存在以下问题:

  1. 缺少输入验证
  2. 错误码使用不一致(404用于格式错误不合适)
  3. 未处理loading状态

建议按照以下方式修改:

-function add(): Promise<any> {
+function add(): Promise<{ code: number; success: boolean; message: string; data?: any[] }> {
   return new Promise((resolve, reject) => {
     try {
+      if (!content.value?.trim()) {
+        reject({
+          code: 400,
+          success: false,
+          message: '请输入JSON内容',
+        });
+        return;
+      }
+
       const parsedArray = JSON.parse(content.value)
 
       if (Array.isArray(parsedArray)) {
+        // 验证数组元素的格式
+        if (!parsedArray.every(item => item && typeof item === 'object')) {
+          reject({
+            code: 400,
+            success: false,
+            message: '数组元素必须是对象类型',
+          });
+          return;
+        }
         resolve({
           code: 200,
           success: true,
           message: 'success',
           data: parsedArray,
         })
         convertArray.value = parsedArray
       } else {
         reject({
-          code: 404,
+          code: 400,
           success: false,
           message: 'Parsed content is not an array',
         })
       }
     } catch (error) {
       reject({
-        code: 404,
+        code: 400,
         success: false,
-        message: 'Invalid JSON format',
+        message: '无效的JSON格式',
       })
     }
   })
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function add(): Promise<any> {
return new Promise((resolve, reject) => {
try {
// 将 JSON 字符串转换为数组
const parsedArray = JSON.parse(content.value)
// 如果转换成功,并且是数组,则返回成功的响应
if (Array.isArray(parsedArray)) {
resolve({
code: 200,
success: true,
message: 'success',
data: parsedArray,
})
convertArray.value = parsedArray
} else {
reject({
code: 404,
success: false,
message: 'Parsed content is not an array',
})
}
} catch (error) {
reject({
code: 404,
success: false,
message: 'Invalid JSON format',
})
}
})
}
function add(): Promise<{ code: number; success: boolean; message: string; data?: any[] }> {
return new Promise((resolve, reject) => {
try {
if (!content.value?.trim()) {
reject({
code: 400,
success: false,
message: '请输入JSON内容',
});
return;
}
const parsedArray = JSON.parse(content.value)
if (Array.isArray(parsedArray)) {
// 验证数组元素的格式
if (!parsedArray.every(item => item && typeof item === 'object')) {
reject({
code: 400,
success: false,
message: '数组元素必须是对象类型',
});
return;
}
resolve({
code: 200,
success: true,
message: 'success',
data: parsedArray,
})
convertArray.value = parsedArray
} else {
reject({
code: 400,
success: false,
message: 'Parsed content is not an array',
})
}
} catch (error) {
reject({
code: 400,
success: false,
message: '无效的JSON格式',
})
}
})
}


defineExpose({
add,
maForm: convertArray,
})

</script>

<template>
<VAceEditor v-model:value="content" lang="json" :theme="theme" class="mt-2 h-500px text-base" />
</template>

<style scoped lang="scss"></style>
97 changes: 97 additions & 0 deletions web/src/components/ma-key-value/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<!--
- MineAdmin is committed to providing solutions for quickly building web applications
- Please view the LICENSE file that was distributed with this source code,
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<[email protected]>
- @Link https://github.com/mineadmin
-->
<i18n lang="yaml">
en:
parse_success: "Parse successful"
add: "Add"
parse_data: "Parse data"
parse: "Parse"
zh_CN:
parse_success: "解析成功"
add: "添加"
parse_data: "解析数据"
parse: "解析"
zh_TW:
parse_success: "解析成功"
add: "添加"
parse_data: "解析數據"
parse: "解析"
</i18n>
<script setup lang="ts">
import type { UseDialogExpose } from '@/hooks/useDialog.ts'
import useDialog from '@/hooks/useDialog.ts'
import KeyValueForm from './components/form.vue'
import { ResultCode } from "@/utils/ResultCode.ts";
import { useMessage } from '@/hooks/useMessage.ts'

defineOptions({ name: 'MaKeyValue' })

const model = defineModel<any>()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

建议添加更具体的类型定义

当前使用 any 类型不够严谨,建议定义具体的接口类型。

建议添加以下类型定义:

+interface KeyValuePair {
+  label: string;
+  value: string;
+}
-const model = defineModel<any>()
+const model = defineModel<KeyValuePair[]>()

Committable suggestion skipped: line range outside the PR's diff.

const t = useTrans().globalTrans
const formRef = ref()
const msg = useMessage()

function addKeyValue() {
model.value = [...model.value, { label: '', value: '' }];
}

// 删除指定索引的键值对
function removeKeyValue(index: number) {
model.value.splice(index, 1) // 使用 splice 删除指定索引的元素
}

// 弹窗配置
const maDialog: UseDialogExpose = useDialog({
alignCenter: true,
// 保存数据
ok: ({ formType: _formType }, _okLoadingState: (state: boolean) => void) => {
formRef.value.add().then((res: any) => {
res.code === ResultCode.SUCCESS ? msg.success(t('parse_success')) : msg.error(res.message);
model.value = [...model.value, ...res.data];
maDialog.close();
}).catch((err: any) => {
msg.alertError(err.message);
});
},
})
</script>

<template>
<div class="w-full">
<div v-if="model?.length > 0" class="w-full flex flex-col gap-2 mb-3">
<div class="flex flex-row justify-between" v-for="(item, index) in model" :key="index">
<div class="key-value-input flex flex-row gap-4">
<el-input v-model="item.label" :placeholder="`label ${index + 1}`" />
<el-input v-model="item.value" :placeholder="`Value ${index + 1}`" />
</div>
<el-button circle type="danger" @click="removeKeyValue(index)">
<ma-svg-icon name="i-heroicons:trash" />
</el-button>
</div>
Comment on lines +69 to +77
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

建议优化键值对输入验证

当前实现没有对输入值进行验证,建议添加必要的验证逻辑。

建议修改为:

 <div class="flex flex-row justify-between" v-for="(item, index) in model" :key="index">
   <div class="key-value-input flex flex-row gap-4">
-    <el-input v-model="item.label" :placeholder="`label ${index + 1}`" />
-    <el-input v-model="item.value" :placeholder="`Value ${index + 1}`" />
+    <el-input
+      v-model="item.label"
+      :placeholder="`label ${index + 1}`"
+      :maxlength="50"
+      show-word-limit
+      @blur="validateKeyValue(index)"
+    />
+    <el-input
+      v-model="item.value"
+      :placeholder="`Value ${index + 1}`"
+      :maxlength="200"
+      show-word-limit
+      @blur="validateKeyValue(index)"
+    />
   </div>
   <el-button circle type="danger" @click="removeKeyValue(index)">
     <ma-svg-icon name="i-heroicons:trash" />
   </el-button>
 </div>

Committable suggestion skipped: line range outside the PR's diff.

</div>
<div>
<el-button type="primary" @click="addKeyValue">{{ t('add') }}</el-button>
<el-button @click="() => {
maDialog.setTitle(t('parse_data'))
maDialog.open({ formType: 'add' })
}">
{{ t('parse') }}
</el-button>
</div>
</div>
<component :is="maDialog.Dialog">
<template #default>
<!-- 新增、编辑表单 -->
<KeyValueForm ref="formRef" />
</template>
</component>
</template>

<style scoped lang="scss"></style>
19 changes: 19 additions & 0 deletions web/src/components/ma-key-value/utils/formatJson.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* MineAdmin is committed to providing solutions for quickly building web applications
* Please view the LICENSE file that was distributed with this source code,
* For the full copyright and license information.
* Thank you very much for using MineAdmin.
*
* @Author X.Mo<[email protected]>
* @Link https://github.com/mineadmin
*/
export default function formatJson(json: Record<string, any>): string {
try {
return JSON.stringify(json, null, 2)
}
catch (error) {
// 如果解析失败,返回原始字符串并附带错误信息
console.error('Invalid JSON string:', error)
return `/* Invalid JSON: ${json} */`
}
}
Loading