fix(i18n): translate backend errors at UI boundaries
This commit is contained in:
1 parent
4422ac8e01
commit
2f44d36018
82 files changed
+1862
-335
No files matched your search
@@ -925,12 +925,12 @@ async function onFileSelected(event: Event) {
|
||||
<template v-else>
|
||||
<div class="flex items-center justify-between px-3 py-2 border-b bg-muted/10">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xs text-muted-foreground">{{ t("dataGenerate.target") }}:</span>
|
||||
<span class="text-xs text-muted-foreground">{{ t("dataGenerate.target") }}:</span>
|
||||
<select v-if="generatedResults.length > 1" v-model="previewTableIndex" class="h-7 rounded border bg-background px-2 text-xs">
|
||||
<option v-for="(r, i) in generatedResults" :key="i" :value="i">{{ r.tableName }}</option>
|
||||
</select>
|
||||
<span v-else class="text-sm font-medium">{{ generatedResults[0].tableName }}</span>
|
||||
<span class="text-xs text-muted-foreground">{{ currentPreview.rows.length }} 行</span>
|
||||
<span class="text-xs text-muted-foreground">{{ t("dataGenerate.previewRowCount", { count: currentPreview.rows.length }) }}</span>
|
||||
</div>
|
||||
<Button variant="outline" size="sm" class="h-7 text-xs" @click="regenerate">{{ t("dataGenerate.regenerate") }}</Button>
|
||||
</div>
|
||||
@@ -1041,7 +1041,7 @@ async function onFileSelected(event: Event) {
|
||||
<Dialog v-model:open="optionsDialogOpen">
|
||||
<DialogContent class="max-w-sm">
|
||||
<DialogHeader>
|
||||
<DialogTitle class="text-sm">生成选项</DialogTitle>
|
||||
<DialogTitle class="text-sm">{{ t("dataGenerate.generateOptions") }}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div class="space-y-3 py-2">
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
|
||||
@@ -4,21 +4,24 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const addressTypes = [
|
||||
{ value: "line1", label: "第1行地址" },
|
||||
{ value: "line2", label: "第2行地址" },
|
||||
{ value: "full", label: "完整地址" },
|
||||
];
|
||||
const { t } = useI18n();
|
||||
|
||||
const regions = [
|
||||
{ value: "us", label: "美国" },
|
||||
{ value: "uk", label: "英国" },
|
||||
{ value: "cn", label: "中国 (English)" },
|
||||
{ value: "jp", label: "日本 (English)" },
|
||||
];
|
||||
const addressTypes = computed(() => [
|
||||
{ value: "line1", label: t("dataGenerate.addressTypes.line1") },
|
||||
{ value: "line2", label: t("dataGenerate.addressTypes.line2") },
|
||||
{ value: "full", label: t("dataGenerate.addressTypes.full") },
|
||||
]);
|
||||
|
||||
const regions = computed(() => [
|
||||
{ value: "us", label: t("dataGenerate.countries.us") },
|
||||
{ value: "uk", label: t("dataGenerate.countries.uk") },
|
||||
{ value: "cn", label: t("dataGenerate.countries.cnEnglish") },
|
||||
{ value: "jp", label: t("dataGenerate.countries.jpEnglish") },
|
||||
]);
|
||||
|
||||
if (!props.params.addressType) props.params.addressType = "line1";
|
||||
if (!props.params.regions) props.params.regions = ["us"];
|
||||
@@ -90,14 +93,14 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">类型</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.typeLabel") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<button v-for="t in addressTypes" :key="t.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.addressType || 'line1') === t.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.addressType = t.value">{{ t.label }}</button>
|
||||
<button v-for="a in addressTypes" :key="a.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.addressType || 'line1') === a.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.addressType = a.value">{{ a.label }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">地区</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.regionLabel") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button v-for="r in regions" :key="r.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.regions ?? []).includes(r.value) ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="toggleRegion(r.value)">{{ r.label }}</button>
|
||||
</div>
|
||||
@@ -105,7 +108,7 @@ function refresh() {
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const barcodeTypes = [
|
||||
{ value: "ean8", label: "EAN8", regex: "[0-9]{8}" },
|
||||
{ value: "ean13", label: "EAN13", regex: "[0-9]{13}" },
|
||||
@@ -78,18 +81,18 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">类型</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.typeLabel") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button v-for="b in barcodeTypes" :key="b.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.barcodeTypes ?? []).includes(b.value) ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="toggleBarcode(b.value)">{{ b.label }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-1">正则表达式</div>
|
||||
<div class="text-xs text-muted-foreground mb-1">{{ t("dataGenerate.regex") }}</div>
|
||||
<div class="font-mono text-xs bg-background rounded border px-2 py-1 break-all">{{ currentRegex }}</div>
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,20 +4,23 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const regions = [
|
||||
{ value: "us", label: "美国" },
|
||||
{ value: "uk", label: "英国" },
|
||||
{ value: "cn", label: "中国" },
|
||||
{ value: "jp", label: "日本" },
|
||||
];
|
||||
const { t } = useI18n();
|
||||
|
||||
const languages = [
|
||||
{ value: "en", label: "English" },
|
||||
{ value: "native", label: "本地语言" },
|
||||
];
|
||||
const regions = computed(() => [
|
||||
{ value: "us", label: t("dataGenerate.countries.us") },
|
||||
{ value: "uk", label: t("dataGenerate.countries.uk") },
|
||||
{ value: "cn", label: t("dataGenerate.countries.cn") },
|
||||
{ value: "jp", label: t("dataGenerate.countries.jp") },
|
||||
]);
|
||||
|
||||
const languages = computed(() => [
|
||||
{ value: "en", label: t("dataGenerate.cityLanguages.english") },
|
||||
{ value: "native", label: t("dataGenerate.cityLanguages.native") },
|
||||
]);
|
||||
|
||||
if (!props.params.regions) props.params.regions = ["us"];
|
||||
if (!props.params.languages) props.params.languages = ["en"];
|
||||
@@ -96,14 +99,14 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">地区</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.regionLabel") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button v-for="r in regions" :key="r.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.regions ?? []).includes(r.value) ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="toggleRegion(r.value)">{{ r.label }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">语言</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.language") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<button v-for="l in languages" :key="l.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.languages?.[0] || 'en') === l.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="setLang(l.value)">{{ l.label }}</button>
|
||||
</div>
|
||||
@@ -111,7 +114,7 @@ function refresh() {
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const dataMap: Record<string, string[]> = {
|
||||
en: ["Red", "Blue", "Green", "Black", "White", "Yellow", "Purple", "Orange", "Pink", "Brown", "Gray", "Cyan", "Jasmine", "Teal", "Maroon", "Navy", "Olive", "Coral"],
|
||||
zh: ["红色", "蓝色", "绿色", "黑色", "白色", "黄色", "紫色", "橙色", "粉色", "棕色", "灰色", "青色", "茉莉色", "蓝绿色", "褐红色", "藏青", "橄榄", "珊瑚色"],
|
||||
@@ -33,7 +36,7 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">语言</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.language") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button
|
||||
v-for="l in [
|
||||
@@ -54,7 +57,7 @@ function refresh() {
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const languageOptions = [
|
||||
{ value: "en", label: "English" },
|
||||
{ value: "zh_pinyin", label: "Chinese (Pinyin)" },
|
||||
@@ -75,7 +78,7 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">语言选择</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.language") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button v-for="l in languageOptions" :key="l.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.languages ?? []).includes(l.value) ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="toggleLang(l.value)">{{ l.label }}</button>
|
||||
</div>
|
||||
@@ -83,7 +86,7 @@ function refresh() {
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -6,9 +6,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const dateFormats = [
|
||||
{ value: "MM/YY", label: "MM/YY" },
|
||||
{ value: "MM/YYYY", label: "MM/YYYY" },
|
||||
@@ -59,25 +62,25 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">日期类型</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.dateType") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<button v-for="f in dateFormats" :key="f.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.ccDateFormat || 'MM/YY') === f.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.ccDateFormat = f.value">{{ f.label }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">年份范围(相对于当前年的偏移量)</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.yearOffsetRange") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<Label class="text-muted-foreground">最小</Label>
|
||||
<Label class="text-muted-foreground">{{ t("dataGenerate.min") }}</Label>
|
||||
<Input v-model.number="params.ccYearOffsetMin" type="number" class="h-6 w-20 text-xs" />
|
||||
<Label class="text-muted-foreground">最大</Label>
|
||||
<Label class="text-muted-foreground">{{ t("dataGenerate.max") }}</Label>
|
||||
<Input v-model.number="params.ccYearOffsetMax" type="number" class="h-6 w-20 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const cardTypes = [
|
||||
{ value: "american_express", label: "American Express" },
|
||||
{ value: "jcb", label: "JCB" },
|
||||
@@ -95,7 +98,7 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">类型选择</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.typeLabel") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button v-for="c in cardTypes" :key="c.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.cardTypes ?? []).includes(c.value) ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="toggleCardType(c.value)">{{ c.label }}</button>
|
||||
</div>
|
||||
@@ -103,7 +106,7 @@ function refresh() {
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const cardTypes = [
|
||||
{ value: "american_express", label: "American Express" },
|
||||
{ value: "jcb", label: "JCB" },
|
||||
@@ -47,7 +50,7 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">类型选择</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.typeLabel") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button v-for="c in cardTypes" :key="c.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.cardTypes ?? []).includes(c.value) ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="toggleCardType(c.value)">{{ c.label }}</button>
|
||||
</div>
|
||||
@@ -55,7 +58,7 @@ function refresh() {
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -26,7 +26,7 @@ const previewVal = computed(() => {
|
||||
return `${year}-${month}-${day}`;
|
||||
});
|
||||
|
||||
const weekdayLabels = ["日", "一", "二", "三", "四", "五", "六"];
|
||||
const weekdayLabels = computed(() => (["sun", "mon", "tue", "wed", "thu", "fri", "sat"] as const).map((d) => t(`dataGenerate.weekdayShort.${d}`)));
|
||||
|
||||
function toggleWeekday(d: number) {
|
||||
if (!props.params.weekdays) props.params.weekdays = [];
|
||||
@@ -77,11 +77,11 @@ function refresh() {
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">星期</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.weekday") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<button type="button" class="px-2 py-1 rounded border text-xs" :class="!params.weekdayMode || params.weekdayMode === 'all' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.weekdayMode = 'all'">全部</button>
|
||||
<button type="button" class="px-2 py-1 rounded border text-xs" :class="params.weekdayMode === 'weekday' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.weekdayMode = 'weekday'">工作日</button>
|
||||
<button type="button" class="px-2 py-1 rounded border text-xs" :class="params.weekdayMode === 'custom' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.weekdayMode = 'custom'">自定义</button>
|
||||
<button type="button" class="px-2 py-1 rounded border text-xs" :class="!params.weekdayMode || params.weekdayMode === 'all' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.weekdayMode = 'all'">{{ t("dataGenerate.all") }}</button>
|
||||
<button type="button" class="px-2 py-1 rounded border text-xs" :class="params.weekdayMode === 'weekday' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.weekdayMode = 'weekday'">{{ t("dataGenerate.weekdayOnly") }}</button>
|
||||
<button type="button" class="px-2 py-1 rounded border text-xs" :class="params.weekdayMode === 'custom' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.weekdayMode = 'custom'">{{ t("dataGenerate.custom") }}</button>
|
||||
</div>
|
||||
<div v-if="params.weekdayMode === 'custom'" class="flex gap-1 mt-2">
|
||||
<button v-for="(_, i) in 7" :key="i" type="button" class="w-7 h-7 rounded text-xs border" :class="(params.weekdays ?? []).includes(i) ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="toggleWeekday(i)">{{ weekdayLabels[i] }}</button>
|
||||
|
||||
@@ -38,7 +38,7 @@ const previewVal = computed(() => {
|
||||
return `${formatLocalDate(d)} ${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
|
||||
});
|
||||
|
||||
const weekdayLabels = ["日", "一", "二", "三", "四", "五", "六"];
|
||||
const weekdayLabels = computed(() => (["sun", "mon", "tue", "wed", "thu", "fri", "sat"] as const).map((d) => t(`dataGenerate.weekdayShort.${d}`)));
|
||||
|
||||
function toggleWeekday(d: number) {
|
||||
if (!props.params.weekdays) props.params.weekdays = [];
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const languages = [
|
||||
{ value: "en", label: "English" },
|
||||
{ value: "zh", label: "简体中文" },
|
||||
@@ -50,7 +53,7 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">语言选择</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.language") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button v-for="l in languages" :key="l.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.languages ?? []).includes(l.value) ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="toggleLang(l.value)">{{ l.label }}</button>
|
||||
</div>
|
||||
@@ -58,7 +61,7 @@ function refresh() {
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -5,9 +5,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const defaultDomains = "gmail.com\noutlook.com\nyahoo.com\nexample.com\ntest.org";
|
||||
|
||||
function pick<T>(arr: T[]): T {
|
||||
@@ -27,7 +30,7 @@ const previewVal = computed(() => {
|
||||
.split("\n")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
if (domains.length === 0) return "(无域名)";
|
||||
if (domains.length === 0) return t("dataGenerate.noDomain");
|
||||
return `${genName()}@${pick(domains)}`;
|
||||
});
|
||||
|
||||
@@ -39,13 +42,13 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">域(Domain)列表</Label>
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">{{ t("dataGenerate.domainList") }}</Label>
|
||||
<textarea v-model="params.emailDomains" rows="4" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" :placeholder="defaultDomains" />
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm break-all">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto shrink-0" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -30,7 +30,7 @@ function refresh() {
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">{{ t("dataGenerate.values") }}</Label>
|
||||
<textarea v-model="params.values" rows="6" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" placeholder="每行一个值 first second third" />
|
||||
<textarea v-model="params.values" rows="6" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" :placeholder="t('dataGenerate.placeholders.enumValues')" />
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const extensionCategoryMap: Record<string, string[]> = {
|
||||
image: [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg", ".webp", ".tiff"],
|
||||
document: [".txt", ".pdf", ".doc", ".docx", ".rtf", ".odt"],
|
||||
@@ -20,18 +23,7 @@ const extensionCategoryMap: Record<string, string[]> = {
|
||||
database: [".db", ".sqlite", ".sql", ".mdb", ".accdb"],
|
||||
};
|
||||
|
||||
const categoryLabels: Record<string, string> = {
|
||||
image: "图像",
|
||||
document: "文档",
|
||||
spreadsheet: "表格",
|
||||
presentation: "演示文稿",
|
||||
audio: "音频",
|
||||
video: "视频",
|
||||
code: "代码",
|
||||
archive: "压缩包",
|
||||
web: "网页",
|
||||
database: "数据库",
|
||||
};
|
||||
const categoryLabels = computed<Record<string, string>>(() => Object.fromEntries((["image", "document", "spreadsheet", "presentation", "audio", "video", "code", "archive", "web", "database"] as const).map((key) => [key, t(`dataGenerate.extensionCategories.${key}`)])));
|
||||
|
||||
if (!props.params.extensionCategory) {
|
||||
props.params.extensionCategory = "image";
|
||||
@@ -56,18 +48,18 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">扩展名类型</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.extensionType") }}</div>
|
||||
<select v-model="params.extensionCategory" class="w-full rounded border bg-background px-2 py-1 text-xs">
|
||||
<option v-for="(label, key) in categoryLabels" :key="key" :value="key">{{ label }}</option>
|
||||
</select>
|
||||
<div class="mt-2 text-xs text-muted-foreground">扩展名列表:</div>
|
||||
<div class="mt-2 text-xs text-muted-foreground">{{ t("dataGenerate.extensionList") }}</div>
|
||||
<div class="mt-1 flex flex-wrap gap-1">
|
||||
<span v-for="ext in extensionCategoryMap[params.extensionCategory || 'image'] || []" :key="ext" class="px-1.5 py-0.5 rounded bg-background border text-xs font-mono">{{ ext }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const extensionCategoryMap: Record<string, string[]> = {
|
||||
image: [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg", ".webp", ".tiff"],
|
||||
document: [".txt", ".pdf", ".doc", ".docx", ".rtf", ".odt"],
|
||||
@@ -20,18 +23,7 @@ const extensionCategoryMap: Record<string, string[]> = {
|
||||
database: [".db", ".sqlite", ".sql", ".mdb", ".accdb"],
|
||||
};
|
||||
|
||||
const categoryLabels: Record<string, string> = {
|
||||
image: "图像",
|
||||
document: "文档",
|
||||
spreadsheet: "表格",
|
||||
presentation: "演示文稿",
|
||||
audio: "音频",
|
||||
video: "视频",
|
||||
code: "代码",
|
||||
archive: "压缩包",
|
||||
web: "网页",
|
||||
database: "数据库",
|
||||
};
|
||||
const categoryLabels = computed<Record<string, string>>(() => Object.fromEntries((["image", "document", "spreadsheet", "presentation", "audio", "video", "code", "archive", "web", "database"] as const).map((key) => [key, t(`dataGenerate.extensionCategories.${key}`)])));
|
||||
|
||||
if (!props.params.includeExtension) {
|
||||
props.params.includeExtension = true;
|
||||
@@ -66,22 +58,22 @@ function refresh() {
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<label class="text-xs text-muted-foreground flex items-center gap-2">
|
||||
<input type="checkbox" v-model="params.includeExtension" class="rounded" />
|
||||
包含扩展名
|
||||
{{ t("dataGenerate.includeExtension") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3" v-if="params.includeExtension">
|
||||
<div class="text-xs text-muted-foreground mb-2">扩展名类型</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.extensionType") }}</div>
|
||||
<select v-model="params.extensionCategory" class="w-full rounded border bg-background px-2 py-1 text-xs">
|
||||
<option v-for="(label, key) in categoryLabels" :key="key" :value="key">{{ label }}</option>
|
||||
</select>
|
||||
<div class="mt-2 text-xs text-muted-foreground">可用扩展名:</div>
|
||||
<div class="mt-2 text-xs text-muted-foreground">{{ t("dataGenerate.availableExtensions") }}</div>
|
||||
<div class="mt-1 flex flex-wrap gap-1">
|
||||
<span v-for="ext in extensionCategoryMap[params.extensionCategory || 'image'] || []" :key="ext" class="px-1.5 py-0.5 rounded bg-background border text-xs font-mono">{{ ext }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const extensionCategoryMap: Record<string, string[]> = {
|
||||
image: [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg", ".webp", ".tiff"],
|
||||
document: [".txt", ".pdf", ".doc", ".docx", ".rtf", ".odt"],
|
||||
@@ -20,18 +23,7 @@ const extensionCategoryMap: Record<string, string[]> = {
|
||||
database: [".db", ".sqlite", ".sql", ".mdb", ".accdb"],
|
||||
};
|
||||
|
||||
const categoryLabels: Record<string, string> = {
|
||||
image: "图像",
|
||||
document: "文档",
|
||||
spreadsheet: "表格",
|
||||
presentation: "演示文稿",
|
||||
audio: "音频",
|
||||
video: "视频",
|
||||
code: "代码",
|
||||
archive: "压缩包",
|
||||
web: "网页",
|
||||
database: "数据库",
|
||||
};
|
||||
const categoryLabels = computed<Record<string, string>>(() => Object.fromEntries((["image", "document", "spreadsheet", "presentation", "audio", "video", "code", "archive", "web", "database"] as const).map((key) => [key, t(`dataGenerate.extensionCategories.${key}`)])));
|
||||
|
||||
if (!props.params.pathTypes) {
|
||||
props.params.pathTypes = ["windows", "macos", "linux"];
|
||||
@@ -104,7 +96,7 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">路径类型</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.pathType") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button
|
||||
v-for="pt in [
|
||||
@@ -125,22 +117,22 @@ function refresh() {
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<label class="text-xs text-muted-foreground flex items-center gap-2">
|
||||
<input type="checkbox" v-model="params.includeFileName" class="rounded" />
|
||||
包含文件名称
|
||||
{{ t("dataGenerate.includeFileName") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">扩展名类型</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.extensionType") }}</div>
|
||||
<select v-model="params.extensionCategory" class="w-full rounded border bg-background px-2 py-1 text-xs">
|
||||
<option v-for="(label, key) in categoryLabels" :key="key" :value="key">{{ label }}</option>
|
||||
</select>
|
||||
<div class="mt-2 text-xs text-muted-foreground">可用扩展名:</div>
|
||||
<div class="mt-2 text-xs text-muted-foreground">{{ t("dataGenerate.availableExtensions") }}</div>
|
||||
<div class="mt-1 flex flex-wrap gap-1">
|
||||
<span v-for="ext in extensionCategoryMap[params.extensionCategory || 'image'] || []" :key="ext" class="px-1.5 py-0.5 rounded bg-background border text-xs font-mono">{{ ext }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm break-all">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import * as api from "@/lib/backend/api";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams; config: any; connectionId?: string; database?: string }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const schemas = ref<string[]>([]);
|
||||
const tables = ref<{ name: string }[]>([]);
|
||||
const columns = ref<{ name: string }[]>([]);
|
||||
@@ -13,11 +16,11 @@ const loadingSchemas = ref(false);
|
||||
const loadingTables = ref(false);
|
||||
const loadingColumns = ref(false);
|
||||
|
||||
const fkModes = [
|
||||
{ value: "random", label: "随机" },
|
||||
{ value: "unique", label: "不重复" },
|
||||
{ value: "repeat", label: "重复每个值" },
|
||||
];
|
||||
const fkModes = computed(() => [
|
||||
{ value: "random", label: t("dataGenerate.genModes.random") },
|
||||
{ value: "unique", label: t("dataGenerate.genModes.unique") },
|
||||
{ value: "repeat", label: t("dataGenerate.genModes.repeat") },
|
||||
]);
|
||||
|
||||
onMounted(async () => {
|
||||
await loadSchemas();
|
||||
@@ -84,30 +87,30 @@ function onTableChange(val: string) {
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3 space-y-2">
|
||||
<div class="grid grid-cols-[60px_1fr] items-center gap-2 text-xs">
|
||||
<Label class="text-muted-foreground">模式</Label>
|
||||
<Label class="text-muted-foreground">{{ t("dataGenerate.schema") }}</Label>
|
||||
<select v-model="props.params.fkSchema" class="h-7 rounded border bg-background px-2 text-xs" @change="onSchemaChange(($event.target as HTMLSelectElement).value)">
|
||||
<option v-if="loadingSchemas" disabled>加载中...</option>
|
||||
<option v-if="loadingSchemas" disabled>{{ t("dataGenerate.loading") }}</option>
|
||||
<option v-for="s in schemas" :key="s" :value="s">{{ s }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="grid grid-cols-[60px_1fr] items-center gap-2 text-xs">
|
||||
<Label class="text-muted-foreground">表</Label>
|
||||
<Label class="text-muted-foreground">{{ t("dataGenerate.tableLabel") }}</Label>
|
||||
<select v-model="props.params.fkTable" class="h-7 rounded border bg-background px-2 text-xs" @change="onTableChange(($event.target as HTMLSelectElement).value)">
|
||||
<option v-if="loadingTables" disabled>加载中...</option>
|
||||
<option v-for="t in tables" :key="t.name" :value="t.name">{{ t.name }}</option>
|
||||
<option v-if="loadingTables" disabled>{{ t("dataGenerate.loading") }}</option>
|
||||
<option v-for="tbl in tables" :key="tbl.name" :value="tbl.name">{{ tbl.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="grid grid-cols-[60px_1fr] items-center gap-2 text-xs">
|
||||
<Label class="text-muted-foreground">字段</Label>
|
||||
<Label class="text-muted-foreground">{{ t("dataGenerate.columnLabel") }}</Label>
|
||||
<select v-model="props.params.fkField" class="h-7 rounded border bg-background px-2 text-xs">
|
||||
<option v-if="loadingColumns" disabled>加载中...</option>
|
||||
<option v-if="loadingColumns" disabled>{{ t("dataGenerate.loading") }}</option>
|
||||
<option v-for="c in columns" :key="c.name" :value="c.name">{{ c.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">生成模式</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.genMode") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs flex-wrap">
|
||||
<button v-for="m in fkModes" :key="m.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.fkMode || 'random') === m.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.fkMode = m.value as 'random' | 'unique' | 'repeat'">
|
||||
{{ m.label }}
|
||||
|
||||
@@ -4,14 +4,17 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const nameFormats: Array<{ value: "full" | "last" | "first"; label: string }> = [
|
||||
{ value: "full", label: "全名" },
|
||||
{ value: "last", label: "仅姓氏" },
|
||||
{ value: "first", label: "仅名字" },
|
||||
];
|
||||
const { t } = useI18n();
|
||||
|
||||
const nameFormats = computed<Array<{ value: "full" | "last" | "first"; label: string }>>(() => [
|
||||
{ value: "full", label: t("dataGenerate.nameFormats.full") },
|
||||
{ value: "last", label: t("dataGenerate.nameFormats.last") },
|
||||
{ value: "first", label: t("dataGenerate.nameFormats.first") },
|
||||
]);
|
||||
|
||||
const languageOptions = [
|
||||
{ value: "en", label: "English" },
|
||||
@@ -78,14 +81,14 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">格式类型</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.nameFormat") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<button v-for="f in nameFormats" :key="f.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.nameFormat || 'full') === f.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.nameFormat = f.value">{{ f.label }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">语言选择</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.language") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button v-for="l in languageOptions" :key="l.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.languages ?? []).includes(l.value) ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="toggleLang(l.value)">{{ l.label }}</button>
|
||||
</div>
|
||||
@@ -93,7 +96,7 @@ function refresh() {
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const languages = [
|
||||
{ value: "en", label: "English" },
|
||||
{ value: "zh", label: "简体中文" },
|
||||
@@ -43,7 +46,7 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">语言选择</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.language") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<button v-for="l in languages" :key="l.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.languages?.[0] || 'en') === l.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="setLang(l.value)">{{ l.label }}</button>
|
||||
</div>
|
||||
@@ -51,7 +54,7 @@ function refresh() {
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -5,9 +5,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
if (!props.params.urlSubdomains) {
|
||||
props.params.urlSubdomains = "auth.\ndrive.\nimage.\nwww.\napi.\nmail.\nshop.\nblog.";
|
||||
}
|
||||
@@ -47,16 +50,16 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">子域</Label>
|
||||
<textarea v-model="params.urlSubdomains" rows="4" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" placeholder="每行一个子域 auth. www. api." />
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">{{ t("dataGenerate.subdomains") }}</Label>
|
||||
<textarea v-model="params.urlSubdomains" rows="4" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" :placeholder="t('dataGenerate.placeholders.subdomains')" />
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">顶级域</Label>
|
||||
<textarea v-model="params.urlTlds" rows="4" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" placeholder="每行一个TLD .com .cn .io" />
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">{{ t("dataGenerate.tlds") }}</Label>
|
||||
<textarea v-model="params.urlTlds" rows="4" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" :placeholder="t('dataGenerate.placeholders.tlds')" />
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm break-all">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -49,7 +49,7 @@ const isGenerate = computed(() => props.params.imageMode !== "folder");
|
||||
<Input v-model="params.folderPath" type="text" placeholder="/path/to/images" class="h-7 text-xs" />
|
||||
</div>
|
||||
<div class="text-xs text-muted-foreground mt-2 mb-1">{{ t("dataGenerate.filterByExtension") }}</div>
|
||||
<textarea v-model="params.fileExtensions" rows="3" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" placeholder="每行一个扩展名 png jpg txt" />
|
||||
<textarea v-model="params.fileExtensions" rows="3" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" :placeholder="t('dataGenerate.placeholders.fileExtensions')" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const languages = [
|
||||
{ value: "en", label: "English" },
|
||||
{ value: "zh", label: "简体中文" },
|
||||
@@ -50,7 +53,7 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">语言选择</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.language") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button v-for="l in languages" :key="l.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.languages ?? []).includes(l.value) ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="toggleLang(l.value)">{{ l.label }}</button>
|
||||
</div>
|
||||
@@ -58,7 +61,7 @@ function refresh() {
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const ipTypes = [
|
||||
{ value: "ipv4", label: "IPv4", regex: "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" },
|
||||
{ value: "ipv6", label: "IPv6", regex: "^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$" },
|
||||
@@ -47,18 +50,18 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">IP地址类型</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.ipType") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<button v-for="t in ipTypes" :key="t.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.ipType || 'ipv4') === t.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.ipType = t.value">{{ t.label }}</button>
|
||||
<button v-for="x in ipTypes" :key="x.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.ipType || 'ipv4') === x.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.ipType = x.value">{{ x.label }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-1">正则表达式</div>
|
||||
<div class="text-xs text-muted-foreground mb-1">{{ t("dataGenerate.regex") }}</div>
|
||||
<div class="font-mono text-xs bg-background rounded border px-2 py-1 break-all">{{ currentRegex }}</div>
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const languages = [
|
||||
{ value: "en", label: "English" },
|
||||
{ value: "zh", label: "简体中文" },
|
||||
@@ -43,7 +46,7 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">语言选择</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.language") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<button v-for="l in languages" :key="l.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.languages?.[0] || 'en') === l.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="setLang(l.value)">{{ l.label }}</button>
|
||||
</div>
|
||||
@@ -51,7 +54,7 @@ function refresh() {
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
if (!props.params.pattern) {
|
||||
props.params.pattern = "([0-9a-f]{2}[:]){5}([0-9a-f]{2})";
|
||||
}
|
||||
@@ -33,12 +36,12 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-1">正则表达式</div>
|
||||
<div class="text-xs text-muted-foreground mb-1">{{ t("dataGenerate.regex") }}</div>
|
||||
<input v-model="params.pattern" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono" />
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const languages = [
|
||||
{ value: "en", label: "English" },
|
||||
{ value: "zh", label: "简体中文" },
|
||||
@@ -43,7 +46,7 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">语言选择</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.language") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<button v-for="l in languages" :key="l.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.languages?.[0] || 'en') === l.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="setLang(l.value)">{{ l.label }}</button>
|
||||
</div>
|
||||
@@ -51,7 +54,7 @@ function refresh() {
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -52,14 +52,14 @@ function refresh() {
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-3 text-xs">
|
||||
<Label class="text-muted-foreground shrink-0">数字类型</Label>
|
||||
<Label class="text-muted-foreground shrink-0">{{ t("dataGenerate.numberType") }}</Label>
|
||||
<div class="flex gap-2">
|
||||
<button type="button" class="px-2 py-1 rounded text-xs border" :class="params.numberType !== 'decimal' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="handleTypeChange('integer')">整数</button>
|
||||
<button type="button" class="px-2 py-1 rounded text-xs border" :class="params.numberType === 'decimal' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="handleTypeChange('decimal')">小数</button>
|
||||
<button type="button" class="px-2 py-1 rounded text-xs border" :class="params.numberType !== 'decimal' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="handleTypeChange('integer')">{{ t("dataGenerate.integer") }}</button>
|
||||
<button type="button" class="px-2 py-1 rounded text-xs border" :class="params.numberType === 'decimal' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="handleTypeChange('decimal')">{{ t("dataGenerate.decimal") }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isDecimal" class="grid grid-cols-[80px_1fr] items-center gap-2 text-xs mt-2">
|
||||
<Label class="text-muted-foreground">小数位数</Label>
|
||||
<Label class="text-muted-foreground">{{ t("dataGenerate.decimalPlaces") }}</Label>
|
||||
<Input v-model.number="params.decimalPlaces" type="number" min="0" max="10" class="h-7 w-20 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,9 +5,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
if (!props.params.values) {
|
||||
props.params.values = "Credit Card\nPayPal\nApple Pay\nGoogle Pay\nBank Transfer\nCash\nCryptocurrency\nAlipay\nWeChat Pay";
|
||||
}
|
||||
@@ -18,7 +21,7 @@ const previewVal = computed(() => {
|
||||
.split("\n")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
if (vals.length === 0) return "(无)";
|
||||
if (vals.length === 0) return t("dataGenerate.noValue");
|
||||
return vals[Math.floor(Math.random() * vals.length)];
|
||||
});
|
||||
|
||||
@@ -31,13 +34,13 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">值列表</Label>
|
||||
<textarea v-model="params.values" rows="6" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" placeholder="每行一个支付方式 Credit Card PayPal Apple Pay" />
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">{{ t("dataGenerate.valueList") }}</Label>
|
||||
<textarea v-model="params.values" rows="6" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" :placeholder="t('dataGenerate.placeholders.paymentMethods')" />
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -11,13 +11,13 @@ const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const regions = [
|
||||
{ value: "us", label: "美国" },
|
||||
{ value: "uk", label: "英国" },
|
||||
{ value: "cn", label: "中国" },
|
||||
{ value: "jp", label: "日本" },
|
||||
{ value: "other", label: "其他" },
|
||||
];
|
||||
const regions = computed(() => [
|
||||
{ value: "us", label: t("dataGenerate.countries.us") },
|
||||
{ value: "uk", label: t("dataGenerate.countries.uk") },
|
||||
{ value: "cn", label: t("dataGenerate.countries.cn") },
|
||||
{ value: "jp", label: t("dataGenerate.countries.jp") },
|
||||
{ value: "other", label: t("dataGenerate.countries.other") },
|
||||
]);
|
||||
|
||||
function randInt(min: number, max: number) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
@@ -77,22 +77,22 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">格式</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.formatLabel") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<button type="button" class="px-2 py-1 rounded border text-xs" :class="(params.phoneFormat || 'domestic') === 'domestic' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.phoneFormat = 'domestic'">国内</button>
|
||||
<button type="button" class="px-2 py-1 rounded border text-xs" :class="params.phoneFormat === 'international' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.phoneFormat = 'international'">国际</button>
|
||||
<button type="button" class="px-2 py-1 rounded border text-xs" :class="(params.phoneFormat || 'domestic') === 'domestic' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.phoneFormat = 'domestic'">{{ t("dataGenerate.phoneDomestic") }}</button>
|
||||
<button type="button" class="px-2 py-1 rounded border text-xs" :class="params.phoneFormat === 'international' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.phoneFormat = 'international'">{{ t("dataGenerate.phoneInternational") }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<input id="phone-sep" type="checkbox" class="h-3.5 w-3.5 accent-primary" :checked="!!params.phoneSeparator" @change="params.phoneSeparator = !params.phoneSeparator" />
|
||||
<Label for="phone-sep" class="text-muted-foreground">包含分隔符</Label>
|
||||
<Label for="phone-sep" class="text-muted-foreground">{{ t("dataGenerate.includeSeparators") }}</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">地区选择</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.regionLabel") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button v-for="r in regions" :key="r.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.phoneRegions ?? []).includes(r.value) ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="toggleRegion(r.value)">{{ r.label }}</button>
|
||||
</div>
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const dataMap: Record<string, string[]> = {
|
||||
en: ["Electronics", "Clothing", "Food", "Books", "Home Goods", "Sports", "Toys", "Beauty", "Furniture", "Automotive", "Garden", "Pet Supplies", "Video Games", "Music Instruments", "Office Supplies"],
|
||||
zh: ["电子", "服装", "食品", "图书", "家居", "运动", "玩具", "美妆", "家具", "汽车用品", "园艺", "宠物用品", "视频游戏", "乐器", "办公用品"],
|
||||
@@ -33,7 +36,7 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">语言</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.language") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button
|
||||
v-for="l in [
|
||||
@@ -54,7 +57,7 @@ function refresh() {
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -5,9 +5,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
if (!props.params.productKeywords) {
|
||||
props.params.productKeywords = "Apple\nCherry\nOrange\nMango\nBanana\nLemon\nGrape\nPeach\nPear\nMelon";
|
||||
}
|
||||
@@ -32,7 +35,7 @@ const previewVal = computed(() => {
|
||||
.split("\n")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
if (keywords.length === 0) return "(无)";
|
||||
if (keywords.length === 0) return t("dataGenerate.noValue");
|
||||
const mode = Math.random();
|
||||
if (mode < 0.4) {
|
||||
return pick(keywords);
|
||||
@@ -51,12 +54,12 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">使用关键字生成</Label>
|
||||
<textarea v-model="params.productKeywords" rows="6" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" placeholder="每行一个关键字 Apple Cherry Orange" />
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">{{ t("dataGenerate.useKeywords") }}</Label>
|
||||
<textarea v-model="params.productKeywords" rows="6" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" :placeholder="t('dataGenerate.placeholders.keywords')" />
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -6,14 +6,17 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const previewKey = ref(0);
|
||||
const previewVal = computed(() => {
|
||||
void previewKey.value;
|
||||
const p = props.params.pattern ?? "";
|
||||
if (!p) return "(未设置)";
|
||||
if (!p) return t("dataGenerate.notSet");
|
||||
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
try {
|
||||
const regex = new RegExp("^" + p + "$");
|
||||
@@ -23,9 +26,9 @@ const previewVal = computed(() => {
|
||||
attempt = Array.from({ length: Math.min(len, 50) }, () => chars[Math.floor(Math.random() * chars.length)]).join("");
|
||||
if (regex.test(attempt)) return attempt;
|
||||
}
|
||||
return attempt || "(无法匹配)";
|
||||
return attempt || t("dataGenerate.noMatch");
|
||||
} catch {
|
||||
return "(无效正则)";
|
||||
return t("dataGenerate.invalidRegex");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -38,18 +41,18 @@ function refresh() {
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="grid grid-cols-[80px_1fr] items-center gap-2 text-xs">
|
||||
<Label class="text-muted-foreground">正则表达式</Label>
|
||||
<Label class="text-muted-foreground">{{ t("dataGenerate.regex") }}</Label>
|
||||
<Input v-model="params.pattern" type="text" placeholder="[A-Za-z0-9]{10}" class="h-7 text-xs font-mono" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-xs mt-2">
|
||||
<input id="regex-raw" type="checkbox" class="h-3.5 w-3.5 accent-primary" :checked="!!params.rawPattern" @change="params.rawPattern = !params.rawPattern" />
|
||||
<Label for="regex-raw" class="text-muted-foreground">原始数据模式</Label>
|
||||
<Label for="regex-raw" class="text-muted-foreground">{{ t("dataGenerate.rawPattern") }}</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm break-all">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto shrink-0" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,21 +4,24 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const formats = [
|
||||
{ value: "name", label: "名称" },
|
||||
{ value: "code", label: "代码" },
|
||||
{ value: "code_name", label: "代码+名称" },
|
||||
];
|
||||
const { t } = useI18n();
|
||||
|
||||
const textTransforms = [
|
||||
{ value: "none", label: "原样" },
|
||||
{ value: "upper", label: "全大写" },
|
||||
{ value: "lower", label: "全小写" },
|
||||
{ value: "title", label: "每个单词首字母大写" },
|
||||
];
|
||||
const formats = computed(() => [
|
||||
{ value: "name", label: t("dataGenerate.regionFormats.name") },
|
||||
{ value: "code", label: t("dataGenerate.regionFormats.code") },
|
||||
{ value: "code_name", label: t("dataGenerate.regionFormats.codeName") },
|
||||
]);
|
||||
|
||||
const textTransforms = computed(() => [
|
||||
{ value: "none", label: t("dataGenerate.transforms.none") },
|
||||
{ value: "upper", label: t("dataGenerate.transforms.upper") },
|
||||
{ value: "lower", label: t("dataGenerate.transforms.lower") },
|
||||
{ value: "title", label: t("dataGenerate.transforms.title") },
|
||||
]);
|
||||
|
||||
const regionData = [
|
||||
{ code: "US", en: "United States", zh: "美国", zh_hant: "美國", ja: "アメリカ合衆国" },
|
||||
@@ -71,13 +74,13 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">格式</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.formatLabel") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<button v-for="f in formats" :key="f.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.regionFormat || 'name') === f.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.regionFormat = f.value">{{ f.label }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">语言</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.language") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button
|
||||
v-for="l in [
|
||||
@@ -97,16 +100,16 @@ function refresh() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">将值转换为</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.transformTo") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<button v-for="t in textTransforms" :key="t.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.textTransform || 'none') === t.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.textTransform = t.value">
|
||||
{{ t.label }}
|
||||
<button v-for="x in textTransforms" :key="x.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.textTransform || 'none') === x.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.textTransform = x.value">
|
||||
{{ x.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -28,7 +28,7 @@ function refresh() {
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="grid grid-cols-[80px_1fr] items-center gap-2 text-xs">
|
||||
<Label class="text-muted-foreground">开始</Label>
|
||||
<Label class="text-muted-foreground">{{ t("dataGenerate.start") }}</Label>
|
||||
<Input v-model.number="params.startValue" type="number" placeholder="1" class="h-7 text-xs" />
|
||||
</div>
|
||||
<div class="grid grid-cols-[80px_1fr] items-center gap-2 text-xs mt-2">
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const dataMap: Record<string, string[]> = {
|
||||
en: ["XS", "S", "M", "L", "XL", "XXL", "XXXL", "One Size"],
|
||||
zh: ["特小", "小", "中", "大", "加大", "特大", "超特大", "均码"],
|
||||
@@ -33,7 +36,7 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">语言</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.language") }}</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button
|
||||
v-for="l in [
|
||||
@@ -54,7 +57,7 @@ function refresh() {
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -5,9 +5,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
if (!props.params.pattern) {
|
||||
props.params.pattern = "([A-F]{2}[-]){2}([0-9]{4}[-])([A-Z])";
|
||||
}
|
||||
@@ -72,12 +75,12 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">正则表达式</Label>
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">{{ t("dataGenerate.regex") }}</Label>
|
||||
<input v-model="params.pattern" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono" placeholder="([A-F]{2}[-]){2}([0-9]{4}[-])([A-Z])" />
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const chars = "abcdefghijklmnopqrstuvwxyz0123456789_";
|
||||
|
||||
const previewKey = ref(0);
|
||||
@@ -26,7 +29,7 @@ function refresh() {
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm break-all">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto shrink-0" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -5,9 +5,12 @@ import { Label } from "@/components/ui/label";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const lorem =
|
||||
"Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua Ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur Excepteur sint occaecat cupidatat non proident sunt in culpa qui officia deserunt mollit anim id est laborum";
|
||||
|
||||
@@ -33,7 +36,7 @@ function refresh() {
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="grid grid-cols-[80px_1fr] items-center gap-2 text-xs">
|
||||
<Label class="text-muted-foreground">字符数</Label>
|
||||
<Label class="text-muted-foreground">{{ t("dataGenerate.charCount") }}</Label>
|
||||
<div class="flex items-center gap-2">
|
||||
<Input v-model.number="params.minLength" type="number" min="1" placeholder="50" class="h-7 w-20 text-xs" />
|
||||
<span class="text-muted-foreground">-</span>
|
||||
@@ -44,7 +47,7 @@ function refresh() {
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-start gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0 mt-0.5">预览</span>
|
||||
<span class="text-muted-foreground shrink-0 mt-0.5">{{ t("dataGenerate.preview") }}</span>
|
||||
<p :key="previewKey" class="font-mono text-xs leading-relaxed break-all">{{ previewVal }}</p>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto shrink-0 mt-0.5" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -4,9 +4,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const languages = [
|
||||
{ value: "en", label: "English" },
|
||||
{ value: "zh", label: "简体中文" },
|
||||
@@ -43,7 +46,7 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">语言选择</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.language") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<button v-for="l in languages" :key="l.value" type="button" class="px-2 py-1 rounded border text-xs" :class="(params.languages?.[0] || 'en') === l.value ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="setLang(l.value)">{{ l.label }}</button>
|
||||
</div>
|
||||
@@ -51,7 +54,7 @@ function refresh() {
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -5,9 +5,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
if (!props.params.urlSubdomains) {
|
||||
props.params.urlSubdomains = "auth.\ndrive.\nimage.\nwww.\napi.\nmail.\nshop.\nblog.";
|
||||
}
|
||||
@@ -49,16 +52,16 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">子域</Label>
|
||||
<textarea v-model="params.urlSubdomains" rows="4" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" placeholder="每行一个子域 auth. www. api." />
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">{{ t("dataGenerate.subdomains") }}</Label>
|
||||
<textarea v-model="params.urlSubdomains" rows="4" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" :placeholder="t('dataGenerate.placeholders.subdomains')" />
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">顶级域</Label>
|
||||
<textarea v-model="params.urlTlds" rows="4" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" placeholder="每行一个TLD .com .cn .io" />
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">{{ t("dataGenerate.tlds") }}</Label>
|
||||
<textarea v-model="params.urlTlds" rows="4" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" :placeholder="t('dataGenerate.placeholders.tlds')" />
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm break-all">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -3,9 +3,12 @@ import { computed, ref } from "vue";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
function genUuid(hyphens: boolean): string {
|
||||
const hex = "0123456789abcdef";
|
||||
const parts = [8, 4, 4, 4, 12];
|
||||
@@ -32,21 +35,21 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-2">UUID格式</div>
|
||||
<div class="text-xs text-muted-foreground mb-2">{{ t("dataGenerate.uuidFormat") }}</div>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<button type="button" class="px-2 py-1 rounded border text-xs" :class="params.uuidHyphens !== false ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.uuidHyphens = true">包含连字符</button>
|
||||
<button type="button" class="px-2 py-1 rounded border text-xs" :class="params.uuidHyphens === false ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.uuidHyphens = false">无格式</button>
|
||||
<button type="button" class="px-2 py-1 rounded border text-xs" :class="params.uuidHyphens !== false ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.uuidHyphens = true">{{ t("dataGenerate.uuidWithHyphens") }}</button>
|
||||
<button type="button" class="px-2 py-1 rounded border text-xs" :class="params.uuidHyphens === false ? 'bg-primary text-primary-foreground border-primary' : 'bg-background'" @click="params.uuidHyphens = false">{{ t("dataGenerate.uuidNoHyphens") }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="text-xs text-muted-foreground mb-1">正则表达式</div>
|
||||
<div class="text-xs text-muted-foreground mb-1">{{ t("dataGenerate.regex") }}</div>
|
||||
<code class="block text-xs font-mono bg-background rounded px-2 py-1 break-all">{{ regex }}</code>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm break-all">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto shrink-0" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
@@ -5,9 +5,12 @@ import { Button } from "@/components/ui/button";
|
||||
import { RefreshCw } from "@lucide/vue";
|
||||
import type { GeneratorParams } from "@/lib/dataGrid/dataGenerate";
|
||||
import CommonOptions from "./CommonOptions.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ params: GeneratorParams }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
if (!props.params.values) {
|
||||
props.params.values = "g\nkg\nlb\noz\nt\nmg\nct";
|
||||
}
|
||||
@@ -23,7 +26,7 @@ const previewVal = computed(() => {
|
||||
.split("\n")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
if (vals.length === 0) return "(无)";
|
||||
if (vals.length === 0) return t("dataGenerate.noValue");
|
||||
return pick(vals);
|
||||
});
|
||||
|
||||
@@ -35,12 +38,12 @@ function refresh() {
|
||||
<template>
|
||||
<div class="space-y-3">
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">值</Label>
|
||||
<textarea v-model="params.values" rows="5" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" placeholder="每行一个单位 g kg lb" />
|
||||
<Label class="text-xs text-muted-foreground mb-1 block">{{ t("dataGenerate.values") }}</Label>
|
||||
<textarea v-model="params.values" rows="5" class="w-full rounded border bg-background px-2 py-1 text-xs font-mono resize-y" :placeholder="t('dataGenerate.placeholders.weightUnits')" />
|
||||
</div>
|
||||
<div class="rounded-md border bg-muted/10 p-3">
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<span class="text-muted-foreground shrink-0">预览</span>
|
||||
<span class="text-muted-foreground shrink-0">{{ t("dataGenerate.preview") }}</span>
|
||||
<span :key="previewKey" class="font-mono text-sm">{{ previewVal }}</span>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 ml-auto" @click="refresh">
|
||||
<RefreshCw class="h-3 w-3" />
|
||||
|
||||
Reference in new issue
Block a user