@hostlink/nuxt-light 1.64.3 → 1.65.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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { computed, ref, useAttrs } from "vue";
|
|
2
|
+
import { computed, ref, useAttrs, getCurrentInstance } from "vue";
|
|
3
3
|
import { useRouter, useRoute } from "vue-router";
|
|
4
|
-
import { useQuasar } from "quasar";
|
|
4
|
+
import { useQuasar, QForm } from "quasar";
|
|
5
5
|
import { model } from "#imports";
|
|
6
6
|
const route = useRoute();
|
|
7
7
|
const router = useRouter();
|
|
@@ -33,17 +33,24 @@ const props = defineProps({
|
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
const loading = ref(false);
|
|
36
|
+
const attrs = useAttrs();
|
|
36
37
|
const emit = defineEmits(["save", "submit", "submitted"]);
|
|
38
|
+
const instance = getCurrentInstance();
|
|
39
|
+
const hasSubmitListener = computed(() => {
|
|
40
|
+
return !!instance?.vnode?.props?.onSubmit;
|
|
41
|
+
});
|
|
37
42
|
const save = async () => {
|
|
43
|
+
if (!form.value) return;
|
|
38
44
|
let valid = await form.value.validate();
|
|
39
45
|
if (!valid) return;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
emit("
|
|
43
|
-
|
|
46
|
+
emit("save");
|
|
47
|
+
if (hasSubmitListener.value) {
|
|
48
|
+
emit("submit", () => {
|
|
49
|
+
loading.value = false;
|
|
50
|
+
});
|
|
51
|
+
return;
|
|
44
52
|
}
|
|
45
|
-
if (props.modelValue) {
|
|
46
|
-
const [module, id_name] = route.name.split("-");
|
|
53
|
+
if (props.modelValue && route.name) {
|
|
47
54
|
try {
|
|
48
55
|
if (route.params[id_name]) {
|
|
49
56
|
if (await model(module).update(parseInt(route.params[id_name]), props.modelValue)) {
|
|
@@ -76,7 +83,6 @@ const onSubmit = (e) => {
|
|
|
76
83
|
e.preventDefault();
|
|
77
84
|
save();
|
|
78
85
|
};
|
|
79
|
-
const attrs = useAttrs();
|
|
80
86
|
const localClass = computed(() => {
|
|
81
87
|
if (attrs.class) {
|
|
82
88
|
return attrs.class;
|
|
@@ -93,7 +99,7 @@ const localClass = computed(() => {
|
|
|
93
99
|
</q-card-section>
|
|
94
100
|
|
|
95
101
|
<q-card-actions align="right">
|
|
96
|
-
<l-btn :icon="submitIcon" :label="submitLabel"
|
|
102
|
+
<l-btn :icon="submitIcon" :label="submitLabel" :loading="loading" type="submit" />
|
|
97
103
|
</q-card-actions>
|
|
98
104
|
</l-card>
|
|
99
105
|
</q-form>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { ref, reactive } from "vue";
|
|
2
|
+
import { ref, reactive, computed } from "vue";
|
|
3
3
|
import { m, q, useAsyncData } from "#imports";
|
|
4
4
|
import { useQuasar } from "quasar";
|
|
5
5
|
import { useI18n } from "vue-i18n";
|
|
@@ -7,29 +7,74 @@ const $q = useQuasar();
|
|
|
7
7
|
const { t } = useI18n();
|
|
8
8
|
const { app } = await q({ app: { languages: true } });
|
|
9
9
|
const splitterModel = ref(62);
|
|
10
|
+
const filter = ref("");
|
|
10
11
|
const { data: all, refresh } = await useAsyncData("translate", () => {
|
|
11
12
|
return q({ allTranslate: true }).then((res) => res.allTranslate);
|
|
12
13
|
});
|
|
14
|
+
const filteredRows = computed(() => {
|
|
15
|
+
if (!filter.value || !all.value) return all.value;
|
|
16
|
+
const searchTerm = filter.value.toLowerCase();
|
|
17
|
+
return all.value.filter((row) => {
|
|
18
|
+
if (row.name?.toLowerCase().includes(searchTerm)) return true;
|
|
19
|
+
for (const language of app.languages) {
|
|
20
|
+
if (row[language.value]?.toLowerCase().includes(searchTerm)) return true;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
});
|
|
24
|
+
});
|
|
13
25
|
const obj = reactive({
|
|
14
|
-
name: ""
|
|
26
|
+
name: "",
|
|
27
|
+
_originalName: ""
|
|
28
|
+
// 用於追蹤原始名稱(編輯模式)
|
|
15
29
|
});
|
|
30
|
+
const isEditMode = computed(() => !!obj._originalName);
|
|
31
|
+
const onSelectRow = (row) => {
|
|
32
|
+
obj.name = row.name;
|
|
33
|
+
obj._originalName = row.name;
|
|
34
|
+
for (const language of app.languages) {
|
|
35
|
+
obj[language.value] = row[language.value] || "";
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const onClearForm = () => {
|
|
39
|
+
obj.name = "";
|
|
40
|
+
obj._originalName = "";
|
|
41
|
+
for (const language of app.languages) {
|
|
42
|
+
obj[language.value] = "";
|
|
43
|
+
}
|
|
44
|
+
};
|
|
16
45
|
const onSave = async () => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
25
|
-
})
|
|
46
|
+
if (isEditMode.value) {
|
|
47
|
+
for (const language of app.languages) {
|
|
48
|
+
await m("updateTranslate", {
|
|
49
|
+
name: obj._originalName,
|
|
50
|
+
language: language.value,
|
|
51
|
+
value: obj[language.value] || ""
|
|
52
|
+
});
|
|
26
53
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
54
|
+
$q.notify({
|
|
55
|
+
message: t("Update success"),
|
|
56
|
+
color: "positive",
|
|
57
|
+
icon: "check"
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
await m("addTranslate", {
|
|
61
|
+
data: {
|
|
62
|
+
name: obj.name,
|
|
63
|
+
values: app.languages.map((language) => {
|
|
64
|
+
return {
|
|
65
|
+
language: language.value,
|
|
66
|
+
value: obj[language.value] || ""
|
|
67
|
+
};
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
$q.notify({
|
|
72
|
+
message: t("Save success"),
|
|
73
|
+
color: "positive",
|
|
74
|
+
icon: "check"
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
onClearForm();
|
|
33
78
|
await refresh();
|
|
34
79
|
};
|
|
35
80
|
const columns = [
|
|
@@ -52,31 +97,25 @@ for (const language of app.languages) {
|
|
|
52
97
|
align: "left"
|
|
53
98
|
});
|
|
54
99
|
}
|
|
55
|
-
const onUpdateTranslate = async (value, language, name) => {
|
|
56
|
-
if (await m("updateTranslate", {
|
|
57
|
-
name,
|
|
58
|
-
language,
|
|
59
|
-
value
|
|
60
|
-
})) {
|
|
61
|
-
$q.notify({
|
|
62
|
-
message: "Update success",
|
|
63
|
-
color: "positive",
|
|
64
|
-
icon: "check"
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
await refresh();
|
|
68
|
-
};
|
|
69
100
|
const onDelete = async (name) => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
101
|
+
$q.dialog({
|
|
102
|
+
title: t("Confirm"),
|
|
103
|
+
message: t("Are you sure you want to delete this item?"),
|
|
104
|
+
cancel: true,
|
|
105
|
+
persistent: true
|
|
106
|
+
}).onOk(async () => {
|
|
107
|
+
if (await m("deleteTranslate", { name })) {
|
|
108
|
+
$q.notify({
|
|
109
|
+
message: t("Delete success"),
|
|
110
|
+
color: "positive",
|
|
111
|
+
icon: "check"
|
|
112
|
+
});
|
|
113
|
+
if (obj._originalName === name) {
|
|
114
|
+
onClearForm();
|
|
115
|
+
}
|
|
116
|
+
await refresh();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
80
119
|
};
|
|
81
120
|
</script>
|
|
82
121
|
|
|
@@ -85,38 +124,115 @@ const onDelete = async (name) => {
|
|
|
85
124
|
<l-card>
|
|
86
125
|
<q-splitter v-model="splitterModel" style="height:680px">
|
|
87
126
|
<template #before>
|
|
88
|
-
<q-table
|
|
89
|
-
:
|
|
127
|
+
<q-table
|
|
128
|
+
:rows="filteredRows"
|
|
129
|
+
flat
|
|
130
|
+
:rows-per-page-options="[0]"
|
|
131
|
+
:columns="columns"
|
|
132
|
+
dense
|
|
133
|
+
separator="cell"
|
|
134
|
+
:bordered="false"
|
|
135
|
+
>
|
|
136
|
+
<template #top>
|
|
137
|
+
<q-input
|
|
138
|
+
v-model="filter"
|
|
139
|
+
dense
|
|
140
|
+
outlined
|
|
141
|
+
:placeholder="t('Search...')"
|
|
142
|
+
clearable
|
|
143
|
+
class="full-width"
|
|
144
|
+
>
|
|
145
|
+
<template #prepend>
|
|
146
|
+
<q-icon name="sym_o_search" />
|
|
147
|
+
</template>
|
|
148
|
+
</q-input>
|
|
149
|
+
</template>
|
|
90
150
|
<template #body="props">
|
|
91
|
-
<q-tr
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
151
|
+
<q-tr
|
|
152
|
+
:props="props"
|
|
153
|
+
:class="{ 'bg-blue-1': obj._originalName === props.row.name }"
|
|
154
|
+
class="cursor-pointer"
|
|
155
|
+
@click="onSelectRow(props.row)"
|
|
156
|
+
>
|
|
157
|
+
<q-td key="_delete" auto-width @click.stop>
|
|
158
|
+
<q-btn dense flat round icon="sym_o_delete" color="negative"
|
|
159
|
+
@click="onDelete(props.row.name)">
|
|
160
|
+
<q-tooltip>{{ t("Delete") }}</q-tooltip>
|
|
161
|
+
</q-btn>
|
|
95
162
|
</q-td>
|
|
96
|
-
<q-td key="name">
|
|
97
|
-
|
|
163
|
+
<q-td key="name" style="max-width: 200px;">
|
|
164
|
+
<div class="text-weight-medium ellipsis">
|
|
165
|
+
{{ props.row.name }}
|
|
166
|
+
<q-tooltip v-if="props.row.name?.length > 25" anchor="top middle" self="bottom middle">
|
|
167
|
+
{{ props.row.name }}
|
|
168
|
+
</q-tooltip>
|
|
169
|
+
</div>
|
|
98
170
|
</q-td>
|
|
99
|
-
<q-td :key="language.value" v-for="language in app.languages" :props="props">
|
|
100
|
-
<div class="text-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
</
|
|
171
|
+
<q-td :key="language.value" v-for="language in app.languages" :props="props" style="max-width: 250px;">
|
|
172
|
+
<div class="text-grey-8 ellipsis-2-lines">
|
|
173
|
+
{{ props.row[language.value] || '-' }}
|
|
174
|
+
<q-tooltip v-if="props.row[language.value]?.length > 50" anchor="top middle" self="bottom middle" max-width="400px">
|
|
175
|
+
<div class="text-pre-wrap">{{ props.row[language.value] }}</div>
|
|
176
|
+
</q-tooltip>
|
|
177
|
+
</div>
|
|
106
178
|
</q-td>
|
|
107
179
|
</q-tr>
|
|
108
180
|
</template>
|
|
109
181
|
</q-table>
|
|
110
|
-
|
|
111
182
|
</template>
|
|
112
183
|
<template #after>
|
|
113
|
-
<
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
184
|
+
<div class="q-pa-md">
|
|
185
|
+
<div class="row items-center q-mb-md">
|
|
186
|
+
<div class="text-h6">
|
|
187
|
+
{{ isEditMode ? t("Edit Translation") : t("Add Translation") }}
|
|
188
|
+
</div>
|
|
189
|
+
<q-space />
|
|
190
|
+
<q-btn
|
|
191
|
+
v-if="isEditMode"
|
|
192
|
+
flat
|
|
193
|
+
dense
|
|
194
|
+
icon="sym_o_add"
|
|
195
|
+
:label="t('New')"
|
|
196
|
+
color="primary"
|
|
197
|
+
@click="onClearForm"
|
|
198
|
+
>
|
|
199
|
+
<q-tooltip>{{ t("Clear form and add new") }}</q-tooltip>
|
|
200
|
+
</q-btn>
|
|
201
|
+
</div>
|
|
202
|
+
<l-form :bordered="false" @submit="onSave">
|
|
203
|
+
<l-input
|
|
204
|
+
label="Name"
|
|
205
|
+
required
|
|
206
|
+
v-model.trim="obj.name"
|
|
207
|
+
clearable
|
|
208
|
+
:disable="isEditMode"
|
|
209
|
+
:hint="isEditMode ? t('Name cannot be changed in edit mode') : ''"
|
|
210
|
+
/>
|
|
211
|
+
<l-input
|
|
212
|
+
v-for="language in app.languages"
|
|
213
|
+
:key="language.value"
|
|
214
|
+
:label="language.name"
|
|
215
|
+
v-model="obj[language.value]"
|
|
216
|
+
type="textarea"
|
|
217
|
+
/>
|
|
218
|
+
<template #buttons>
|
|
219
|
+
<q-btn
|
|
220
|
+
v-if="isEditMode"
|
|
221
|
+
flat
|
|
222
|
+
:label="t('Cancel')"
|
|
223
|
+
@click="onClearForm"
|
|
224
|
+
class="q-mr-sm"
|
|
225
|
+
/>
|
|
226
|
+
<l-save-btn :label="isEditMode ? t('Update') : t('Save')" />
|
|
227
|
+
</template>
|
|
228
|
+
</l-form>
|
|
229
|
+
</div>
|
|
118
230
|
</template>
|
|
119
231
|
</q-splitter>
|
|
120
232
|
</l-card>
|
|
121
233
|
</l-page>
|
|
122
234
|
</template>
|
|
235
|
+
|
|
236
|
+
<style scoped>
|
|
237
|
+
.ellipsis{white-space:nowrap}.ellipsis,.ellipsis-2-lines{overflow:hidden;text-overflow:ellipsis}.ellipsis-2-lines{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;word-break:break-word}
|
|
238
|
+
</style>
|