@hostlink/nuxt-light 1.30.0 → 1.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +1 -1
- package/dist/runtime/components/l-customizer.vue +1 -0
- package/dist/runtime/components/l-input.vue +10 -10
- package/dist/runtime/components/l-input.vue.d.ts +15 -3
- package/dist/runtime/components/l-table.vue +10 -0
- package/dist/runtime/pages/MailLog/index.vue +16 -5
- package/dist/runtime/pages/Permission/all.vue +49 -43
- package/dist/runtime/pages/System/database/backup.vue +21 -19
- package/dist/runtime/pages/System/database/process.vue +11 -13
- package/dist/runtime/pages/System/database/table.vue +12 -12
- package/dist/runtime/pages/User/setting/style.vue +1 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -60,8 +60,8 @@ const props = defineProps({
|
|
|
60
60
|
onBlur: { type: Function, required: false },
|
|
61
61
|
onClear: { type: Function, required: false }
|
|
62
62
|
});
|
|
63
|
-
const modelValue =
|
|
64
|
-
const new_rules = props.rules
|
|
63
|
+
const modelValue = defineModel();
|
|
64
|
+
const new_rules = Array.isArray(props.rules) ? [...props.rules] : [];
|
|
65
65
|
if (props.required) {
|
|
66
66
|
new_rules.push((val) => !!val || t("input_required", [t(props.label ?? "")]));
|
|
67
67
|
}
|
|
@@ -72,28 +72,29 @@ if (props.type == "email") {
|
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
const stringRules = new_rules.filter((r) => typeof r === "string");
|
|
76
|
+
if (stringRules.includes("containUpper")) {
|
|
76
77
|
new_rules.push((val) => {
|
|
77
78
|
if (val && !val.match(/[A-Z]/)) {
|
|
78
79
|
return t("Must contain at least one uppercase letter");
|
|
79
80
|
}
|
|
80
81
|
});
|
|
81
82
|
}
|
|
82
|
-
if (
|
|
83
|
+
if (stringRules.includes("containLower")) {
|
|
83
84
|
new_rules.push((val) => {
|
|
84
85
|
if (val && !val.match(/[a-z]/)) {
|
|
85
86
|
return t("Must contain at least one lowercase letter");
|
|
86
87
|
}
|
|
87
88
|
});
|
|
88
89
|
}
|
|
89
|
-
if (
|
|
90
|
+
if (stringRules.includes("containNumber")) {
|
|
90
91
|
new_rules.push((val) => {
|
|
91
92
|
if (val && !val.match(/[0-9]/)) {
|
|
92
93
|
return t("Must contain at least one number");
|
|
93
94
|
}
|
|
94
95
|
});
|
|
95
96
|
}
|
|
96
|
-
if (
|
|
97
|
+
if (stringRules.includes("containSpecial")) {
|
|
97
98
|
new_rules.push((val) => {
|
|
98
99
|
if (val && !val.match(/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/)) {
|
|
99
100
|
return t("Must contain at least one symbol");
|
|
@@ -154,10 +155,9 @@ const onClickTc2Sc = () => {
|
|
|
154
155
|
</template>
|
|
155
156
|
|
|
156
157
|
<template v-if="localShowPassword" v-slot:append>
|
|
157
|
-
<q-icon name="sym_o_visibility" class="cursor-pointer"
|
|
158
|
-
|
|
159
|
-
<q-icon name="sym_o_visibility_off" class="cursor-pointer"
|
|
160
|
-
@click="isShowPassword = true" v-else />
|
|
158
|
+
<q-icon name="sym_o_visibility" class="cursor-pointer" @click="isShowPassword = false"
|
|
159
|
+
v-if="isShowPassword" />
|
|
160
|
+
<q-icon name="sym_o_visibility_off" class="cursor-pointer" @click="isShowPassword = true" v-else />
|
|
161
161
|
</template>
|
|
162
162
|
|
|
163
163
|
</q-input>
|
|
@@ -4,23 +4,31 @@ export interface LInputProps extends QInputProps {
|
|
|
4
4
|
translate?: boolean;
|
|
5
5
|
required?: boolean;
|
|
6
6
|
}
|
|
7
|
+
type __VLS_Props = LInputProps;
|
|
7
8
|
declare const new_rules: import("quasar").ValidationRule[];
|
|
8
9
|
declare const isShowPassword: import("vue").Ref<boolean, boolean>;
|
|
9
10
|
declare const localType: import("vue").ComputedRef<"number" | "textarea" | "time" | "text" | "search" | "date" | "url" | "email" | "file" | "datetime-local" | "password" | "tel" | undefined>;
|
|
10
11
|
declare const localShowPassword: import("vue").ComputedRef<boolean>;
|
|
11
12
|
declare const onClickTc2Sc: () => void;
|
|
13
|
+
type __VLS_PublicProps = __VLS_Props & {
|
|
14
|
+
modelValue?: any;
|
|
15
|
+
};
|
|
12
16
|
declare const __VLS_ctx: InstanceType<__VLS_PickNotAny<typeof __VLS_self, new () => {}>>;
|
|
13
17
|
declare var __VLS_31: string | number, __VLS_32: any;
|
|
14
18
|
type __VLS_Slots = __VLS_PrettifyGlobal<__VLS_OmitStringIndex<typeof __VLS_ctx.$slots> & {
|
|
15
19
|
[K in NonNullable<typeof __VLS_31>]?: (props: typeof __VLS_32) => any;
|
|
16
20
|
}>;
|
|
17
|
-
declare const __VLS_self: import("vue").DefineComponent<
|
|
21
|
+
declare const __VLS_self: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
18
22
|
new_rules: typeof new_rules;
|
|
19
23
|
isShowPassword: typeof isShowPassword;
|
|
20
24
|
localType: typeof localType;
|
|
21
25
|
localShowPassword: typeof localShowPassword;
|
|
22
26
|
onClickTc2Sc: typeof onClickTc2Sc;
|
|
23
|
-
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
27
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
28
|
+
"update:modelValue": (value: any) => any;
|
|
29
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
30
|
+
"onUpdate:modelValue"?: ((value: any) => any) | undefined;
|
|
31
|
+
}>, {
|
|
24
32
|
dense: boolean;
|
|
25
33
|
dark: boolean | null;
|
|
26
34
|
rounded: boolean;
|
|
@@ -31,7 +39,11 @@ declare const __VLS_self: import("vue").DefineComponent<LInputProps, {
|
|
|
31
39
|
standout: string | boolean;
|
|
32
40
|
stackLabel: boolean;
|
|
33
41
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
34
|
-
declare const __VLS_component: import("vue").DefineComponent<
|
|
42
|
+
declare const __VLS_component: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
43
|
+
"update:modelValue": (value: any) => any;
|
|
44
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
45
|
+
"onUpdate:modelValue"?: ((value: any) => any) | undefined;
|
|
46
|
+
}>, {
|
|
35
47
|
dense: boolean;
|
|
36
48
|
dark: boolean | null;
|
|
37
49
|
rounded: boolean;
|
|
@@ -240,12 +240,22 @@ const onLocalRequest = async (p) => {
|
|
|
240
240
|
pagination.value.descending = p.pagination.descending;
|
|
241
241
|
pagination.value.rowsPerPage = p.pagination.rowsPerPage;
|
|
242
242
|
loading.value = false;
|
|
243
|
+
if (modelName.value) {
|
|
244
|
+
localStorage.setItem(`l-table-rowsPerPage-${modelName.value}`, String(pagination.value.rowsPerPage));
|
|
245
|
+
}
|
|
243
246
|
validateData();
|
|
244
247
|
},
|
|
245
248
|
loadObjects(model2, filters2 = null, fields = []) {
|
|
246
249
|
return this.loadData(model2, filters2, fields);
|
|
247
250
|
},
|
|
248
251
|
async loadData(model2, filters2 = null, fields = []) {
|
|
252
|
+
const saved = Number(localStorage.getItem(`l-table-rowsPerPage-${model2}`));
|
|
253
|
+
if (saved && props.rowsPerPageOptions.includes(saved) && saved !== pagination.value.rowsPerPage) {
|
|
254
|
+
pagination.value.rowsPerPage = saved;
|
|
255
|
+
if (typeof p !== "undefined" && p.pagination) {
|
|
256
|
+
p.pagination.rowsPerPage = saved;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
249
259
|
fields.forEach((f) => {
|
|
250
260
|
builder.add(f);
|
|
251
261
|
});
|
|
@@ -8,17 +8,28 @@ const columns = model("MailLog").columns({
|
|
|
8
8
|
created_time: true,
|
|
9
9
|
body: true
|
|
10
10
|
});
|
|
11
|
+
function requestMailLog(event) {
|
|
12
|
+
event.loadObjects("MailLog", {}, ["body"]);
|
|
13
|
+
}
|
|
11
14
|
</script>
|
|
12
15
|
|
|
13
16
|
<template>
|
|
14
17
|
<l-page>
|
|
15
|
-
<l-table
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
<l-table
|
|
19
|
+
row-key="maillog_id"
|
|
20
|
+
@request-data="requestMailLog"
|
|
21
|
+
:columns="columns"
|
|
22
|
+
sort-by="maillog_id:desc"
|
|
23
|
+
>
|
|
18
24
|
<template #row-expand="props">
|
|
19
|
-
<iframe
|
|
25
|
+
<iframe
|
|
26
|
+
width="100%"
|
|
27
|
+
height="300px"
|
|
28
|
+
:srcdoc="props.row.body"
|
|
29
|
+
frameborder="0"
|
|
30
|
+
sandbox
|
|
31
|
+
></iframe>
|
|
20
32
|
</template>
|
|
21
|
-
|
|
22
33
|
</l-table>
|
|
23
34
|
</l-page>
|
|
24
35
|
</template>
|
|
@@ -1,67 +1,73 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import {
|
|
2
|
+
import { computed, ref } from "vue";
|
|
3
3
|
import { m, api } from "#imports";
|
|
4
4
|
import { useI18n } from "vue-i18n";
|
|
5
5
|
const { t } = useI18n();
|
|
6
|
-
const
|
|
7
|
-
app: {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
const fetchApp = async () => {
|
|
7
|
+
const { app: app2 } = await api.query({
|
|
8
|
+
app: {
|
|
9
|
+
permissions: true,
|
|
10
|
+
roles: {
|
|
11
|
+
name: true,
|
|
12
|
+
permissions: true
|
|
13
|
+
}
|
|
12
14
|
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
align: "left"
|
|
20
|
-
|
|
21
|
-
roles.forEach((role) => {
|
|
22
|
-
columns.push({
|
|
15
|
+
});
|
|
16
|
+
return app2;
|
|
17
|
+
};
|
|
18
|
+
const app = ref(await fetchApp());
|
|
19
|
+
const roles = computed(() => app.value.roles);
|
|
20
|
+
const columns = computed(() => [
|
|
21
|
+
{ label: t("Permission"), field: "permission", align: "left" },
|
|
22
|
+
...roles.value.map((role) => ({
|
|
23
23
|
label: role.name,
|
|
24
24
|
field: role.name,
|
|
25
25
|
align: "left"
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
const rows =
|
|
29
|
-
app.permissions.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
rows.push(row);
|
|
41
|
-
});
|
|
42
|
-
const onUpdate = (value, role, permission) => {
|
|
26
|
+
}))
|
|
27
|
+
]);
|
|
28
|
+
const rows = computed(
|
|
29
|
+
() => app.value.permissions.map((permission) => {
|
|
30
|
+
const row = { permission };
|
|
31
|
+
roles.value.forEach((role) => {
|
|
32
|
+
row[role.name] = role.permissions.includes(permission);
|
|
33
|
+
});
|
|
34
|
+
return row;
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
const onUpdate = async (value, role, permission) => {
|
|
43
38
|
if (value) {
|
|
44
|
-
m("addPermission", { value: permission, role });
|
|
39
|
+
await m("addPermission", { value: permission, role });
|
|
45
40
|
} else {
|
|
46
|
-
m("removePermission", { value: permission, role });
|
|
41
|
+
await m("removePermission", { value: permission, role });
|
|
47
42
|
}
|
|
48
|
-
|
|
49
|
-
if (row.permission == permission) {
|
|
50
|
-
row[role] = value;
|
|
51
|
-
}
|
|
52
|
-
});
|
|
43
|
+
app.value = await fetchApp();
|
|
53
44
|
};
|
|
45
|
+
const filter = ref("");
|
|
46
|
+
const filteredRows = computed(() => {
|
|
47
|
+
if (!filter.value) return rows.value;
|
|
48
|
+
return rows.value.filter(
|
|
49
|
+
(row) => row.permission.toLowerCase().includes(filter.value.toLowerCase())
|
|
50
|
+
);
|
|
51
|
+
});
|
|
54
52
|
</script>
|
|
55
53
|
|
|
56
54
|
<template>
|
|
57
55
|
<l-page>
|
|
58
|
-
<q-table :columns="columns" flat bordered :rows="
|
|
56
|
+
<q-table :columns="columns" flat bordered :rows="filteredRows" :pagination="{ rowsPerPage: 0 }" dense>
|
|
57
|
+
<template #top-left>
|
|
58
|
+
<q-input :color="$light.color" v-model="filter" :placeholder="t('Filter permissions')" dense clearable
|
|
59
|
+
outlined>
|
|
60
|
+
<template v-slot:append>
|
|
61
|
+
<q-icon name="sym_o_search" />
|
|
62
|
+
</template>
|
|
63
|
+
</q-input>
|
|
64
|
+
</template>
|
|
59
65
|
<template #body="props">
|
|
60
66
|
<q-tr :props="props">
|
|
61
67
|
<q-td>
|
|
62
68
|
{{ props.row.permission }}
|
|
63
69
|
</q-td>
|
|
64
|
-
<q-td v-for="role in roles">
|
|
70
|
+
<q-td v-for="role in roles" :key="role.name">
|
|
65
71
|
<q-checkbox v-model="props.row[role.name]"
|
|
66
72
|
@update:model-value="onUpdate($event, role.name, props.row.permission)"
|
|
67
73
|
:color="$light.color" />
|
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { q } from "#imports";
|
|
3
|
-
import { Loading } from "quasar";
|
|
3
|
+
import { Loading, Notify } from "quasar";
|
|
4
4
|
const onClickDownload = async () => {
|
|
5
|
-
Loading.show({
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
Loading.show({ message: "Exporting database..." });
|
|
6
|
+
try {
|
|
7
|
+
const data = await q({
|
|
8
|
+
system: {
|
|
9
|
+
database: {
|
|
10
|
+
export: true
|
|
11
|
+
}
|
|
12
12
|
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
});
|
|
14
|
+
const blob = new Blob([data.system.database.export], { type: "text/plain;charset=utf-8" });
|
|
15
|
+
const url = URL.createObjectURL(blob);
|
|
16
|
+
const a = document.createElement("a");
|
|
17
|
+
a.download = "backup.sql";
|
|
18
|
+
a.href = url;
|
|
19
|
+
a.click();
|
|
20
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
Notify.create({ type: "negative", message: "\u4E0B\u8F09\u5931\u6557\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66\u3002" });
|
|
23
|
+
} finally {
|
|
24
|
+
Loading.hide();
|
|
25
|
+
}
|
|
24
26
|
};
|
|
25
27
|
</script>
|
|
26
28
|
|
|
@@ -15,28 +15,28 @@ const { data, refresh } = await useAsyncData(async () => {
|
|
|
15
15
|
return system;
|
|
16
16
|
});
|
|
17
17
|
const autoRefresh = ref(0);
|
|
18
|
-
|
|
18
|
+
const interval = ref(null);
|
|
19
19
|
watch(autoRefresh, (value) => {
|
|
20
|
-
if (interval)
|
|
21
|
-
clearInterval(interval);
|
|
20
|
+
if (interval.value)
|
|
21
|
+
clearInterval(interval.value);
|
|
22
22
|
if (value) {
|
|
23
|
-
interval = setInterval(
|
|
23
|
+
interval.value = setInterval(() => {
|
|
24
|
+
refresh();
|
|
25
|
+
}, value);
|
|
24
26
|
}
|
|
25
|
-
});
|
|
27
|
+
}, { immediate: true });
|
|
26
28
|
onUnmounted(() => {
|
|
27
|
-
if (interval)
|
|
28
|
-
clearInterval(interval);
|
|
29
|
+
if (interval.value)
|
|
30
|
+
clearInterval(interval.value);
|
|
29
31
|
});
|
|
30
32
|
</script>
|
|
31
33
|
|
|
32
34
|
<template>
|
|
33
35
|
<l-page title="System Database Process">
|
|
34
|
-
<q-table :rows="data
|
|
36
|
+
<q-table :rows="data?.database?.processList || []" hide-bottom v-bind="$light.styles.table" :loading="loading">
|
|
35
37
|
<template #top-left>
|
|
36
38
|
<q-btn label="Reload" outline :color="$light.color" icon="sym_o_refresh" @click="refresh"></q-btn>
|
|
37
|
-
|
|
38
39
|
</template>
|
|
39
|
-
|
|
40
40
|
<template #top-right>
|
|
41
41
|
<q-select label="Auto Refresh" v-model="autoRefresh" :options="[
|
|
42
42
|
{ label: 'Off', value: 0 },
|
|
@@ -48,11 +48,9 @@ onUnmounted(() => {
|
|
|
48
48
|
{ label: '10m', value: 600000 },
|
|
49
49
|
{ label: '30m', value: 1800000 },
|
|
50
50
|
{ label: '1h', value: 3600000 }
|
|
51
|
-
]" @
|
|
51
|
+
]" @update:model-value="autoRefresh && refresh()" outlined :color="$light.color" dense style="min-width: 200px"
|
|
52
52
|
map-options emit-value></q-select>
|
|
53
|
-
|
|
54
53
|
</template>
|
|
55
54
|
</q-table>
|
|
56
|
-
|
|
57
55
|
</l-page>
|
|
58
56
|
</template>
|
|
@@ -16,10 +16,10 @@ const { data, refresh } = await useAsyncData("database", async () => {
|
|
|
16
16
|
});
|
|
17
17
|
const SYSTEM_TABLES = ["Config", "EventLog", "MailLog", "Permission", "Role", "SystemValue", "Translate", "User", "UserLog", "UserRole", "MyFavorite", "CustomField"];
|
|
18
18
|
const custom_tables = computed(() => {
|
|
19
|
-
return data.value
|
|
19
|
+
return (data.value?.tableStatus ?? []).filter((table) => !SYSTEM_TABLES.includes(table.Name));
|
|
20
20
|
});
|
|
21
21
|
const systables = computed(() => {
|
|
22
|
-
return data.value
|
|
22
|
+
return (data.value?.tableStatus ?? []).filter((table) => SYSTEM_TABLES.includes(table.Name));
|
|
23
23
|
});
|
|
24
24
|
const field_add = resolveComponent("l-dialog-database-field-add");
|
|
25
25
|
const table_add = resolveComponent("l-database-create-table-dialog");
|
|
@@ -51,8 +51,10 @@ const add = async (table) => {
|
|
|
51
51
|
});
|
|
52
52
|
};
|
|
53
53
|
const selected = reactive({});
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
if (data.value?.table) {
|
|
55
|
+
for (let table of data.value.table) {
|
|
56
|
+
selected[table.name] = [];
|
|
57
|
+
}
|
|
56
58
|
}
|
|
57
59
|
const removeField = async (table) => {
|
|
58
60
|
light.dialog({
|
|
@@ -173,14 +175,14 @@ const truncatTable = async () => {
|
|
|
173
175
|
</l-list>
|
|
174
176
|
</l-card>
|
|
175
177
|
|
|
176
|
-
<q-card
|
|
178
|
+
<q-card>
|
|
177
179
|
<q-list separator bordered>
|
|
178
180
|
<q-expansion-item label="Table Status" dense>
|
|
179
|
-
<div
|
|
181
|
+
<div s>
|
|
180
182
|
|
|
181
183
|
<q-table :rows="custom_tables" hide-pagination flat bordered separator="cell" dense
|
|
182
184
|
:rows-per-page-options="[0]" v-model:selected="selectedTable" selection="multiple"
|
|
183
|
-
row-key="Name">
|
|
185
|
+
row-key="Name" :color="$light.color">
|
|
184
186
|
|
|
185
187
|
<template #top-left>
|
|
186
188
|
<q-btn icon="sym_o_add" @click="createTable()" round flat size="sm">
|
|
@@ -210,13 +212,12 @@ const truncatTable = async () => {
|
|
|
210
212
|
</q-list>
|
|
211
213
|
</q-card>
|
|
212
214
|
|
|
213
|
-
<q-card
|
|
215
|
+
<q-card>
|
|
214
216
|
<q-list separator bordered>
|
|
215
217
|
<q-expansion-item :label="table.name" v-for="table in data.table" dense>
|
|
216
218
|
<div class="q-ma-sm">
|
|
217
|
-
<q-table row-key="Field"
|
|
218
|
-
:
|
|
219
|
-
v-model:selected="selected[table.name]">
|
|
219
|
+
<q-table row-key="Field" :rows="table.columns" :rows-per-page-options="[0]" hide-pagination flat
|
|
220
|
+
bordered selection="multiple" v-model:selected="selected[table.name]" :color="$light.color">
|
|
220
221
|
<template #top-left>
|
|
221
222
|
<q-btn icon="sym_o_add" @click="add(table.name)" round flat size="sm">
|
|
222
223
|
<q-tooltip>Add field</q-tooltip>
|
|
@@ -233,6 +234,5 @@ const truncatTable = async () => {
|
|
|
233
234
|
</q-list>
|
|
234
235
|
</q-card>
|
|
235
236
|
|
|
236
|
-
|
|
237
237
|
</l-page>
|
|
238
238
|
</template>
|