changes
This commit is contained in:
@@ -22,22 +22,15 @@
|
||||
v-if="title"
|
||||
class="modal-card-head px-4 py-3"
|
||||
>
|
||||
<div class="w-full">
|
||||
<div class="field is-grouped is-align-items-center">
|
||||
<div class="control is-expanded has-text-left">
|
||||
<p
|
||||
class="fs-17 font-semibold has-text-primary"
|
||||
class="fs-17 font-semibold has-text-primary control is-expanded has-text-left modal-card-title"
|
||||
v-html="title"
|
||||
></p>
|
||||
</div>
|
||||
<div class="control has-text-right">
|
||||
<button
|
||||
class="delete is-medium"
|
||||
aria-label="close"
|
||||
@click="closeModal()"
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<section
|
||||
class="modal-card-body p-4"
|
||||
@@ -104,7 +97,7 @@ const docid = $id();
|
||||
let count = 0;
|
||||
const lock = false;
|
||||
|
||||
const closeModal = function () {
|
||||
const closeModal = () => {
|
||||
if (!lock) emit("close");
|
||||
};
|
||||
|
||||
@@ -129,7 +122,8 @@ const doClick = function (e) {
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (Object.values(props).some((x) => isNotNil(x))) {
|
||||
const modalHasProps = Object.values(props).some((x) => isNotNil(x));
|
||||
if (modalHasProps) {
|
||||
document.documentElement.classList.add("is-clipped");
|
||||
window.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape") closeModal();
|
||||
|
||||
@@ -34,13 +34,13 @@
|
||||
<span class="icon is-left">
|
||||
<Icon
|
||||
name="material-symbols:search"
|
||||
:size="22"
|
||||
:size="20"
|
||||
/>
|
||||
</span>
|
||||
<span class="icon is-right">
|
||||
<Icon
|
||||
name="material-symbols:keyboard-arrow-down-rounded"
|
||||
:size="22"
|
||||
:size="20"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
@@ -54,14 +54,8 @@
|
||||
class="dropdown-content px-3"
|
||||
style="min-width: 100%"
|
||||
>
|
||||
<p
|
||||
v-if="data.length === 0"
|
||||
class="has-text-grey px-2 py-1"
|
||||
>
|
||||
{{ isVietnamese ? "Không có giá trị thỏa mãn" : "No matching values" }}
|
||||
</p>
|
||||
<ScrollBox
|
||||
v-else
|
||||
v-if="data.length > 0"
|
||||
v-bind="{
|
||||
data: data,
|
||||
name: field,
|
||||
@@ -71,34 +65,55 @@
|
||||
}"
|
||||
@selected="choose"
|
||||
/>
|
||||
<p
|
||||
v-else
|
||||
class="has-text-grey px-2 py-1"
|
||||
>
|
||||
<Icon
|
||||
v-if="isLoading"
|
||||
name="svg-spinners:90-ring"
|
||||
:size="22"
|
||||
/>
|
||||
<span v-else>Không có giá trị thoả mãn</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="control"
|
||||
v-if="clearable && value"
|
||||
class="control"
|
||||
>
|
||||
<button
|
||||
class="button is-primary px-2"
|
||||
class="button is-light"
|
||||
@click="clearValue"
|
||||
style="height: 100%"
|
||||
type="button"
|
||||
>
|
||||
<SvgIcon v-bind="{ name: 'close.svg', type: 'white', size: 24 }"></SvgIcon>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:close-rounded"
|
||||
:size="16"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="control"
|
||||
v-if="viewaddon"
|
||||
class="control"
|
||||
>
|
||||
<button
|
||||
class="button is-dark px-2"
|
||||
class="button is-primary is-light"
|
||||
@click="viewInfo()"
|
||||
style="height: 100%"
|
||||
type="button"
|
||||
>
|
||||
<SvgIcon v-bind="{ name: 'view.svg', type: 'white', size: 24 }"></SvgIcon>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:visibility-rounded"
|
||||
:size="20"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
@@ -106,15 +121,15 @@
|
||||
v-if="addon"
|
||||
>
|
||||
<button
|
||||
class="button is-primary"
|
||||
class="button is-success is-light"
|
||||
@click="addNew()"
|
||||
style="height: 100%"
|
||||
type="button"
|
||||
>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:add-2-rounded"
|
||||
:size="22"
|
||||
name="material-symbols:add-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
@@ -124,8 +139,7 @@
|
||||
@dataevent="dataevent"
|
||||
@close="showmodal = undefined"
|
||||
v-bind="showmodal"
|
||||
v-if="showmodal"
|
||||
></Modal>
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -157,6 +171,7 @@ export default {
|
||||
return {
|
||||
search: undefined,
|
||||
data: [],
|
||||
isLoading: false,
|
||||
timer: undefined,
|
||||
value: undefined,
|
||||
selected: undefined,
|
||||
@@ -266,8 +281,10 @@ export default {
|
||||
}
|
||||
},
|
||||
async getData() {
|
||||
this.isLoading = false;
|
||||
this.params.filter = this.filter;
|
||||
let data = await this.$getdata(this.api, undefined, this.params);
|
||||
this.isLoading = true;
|
||||
return data;
|
||||
},
|
||||
async getApi(val) {
|
||||
@@ -373,4 +390,11 @@ export default {
|
||||
.field:not(:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.button.is-light {
|
||||
--bulma-button-background-l: 89%;
|
||||
}
|
||||
.button:hover,
|
||||
.button.is-hovered {
|
||||
--bulma-button-background-l-delta: -10%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<template>
|
||||
<div class="control has-icons-left">
|
||||
<div class="field has-addons">
|
||||
<p class="control has-icons-left has-icons-right is-expanded">
|
||||
<input
|
||||
type="text"
|
||||
v-model="value"
|
||||
@keyup="doCheck"
|
||||
:class="['input', disabled && 'has-text-black']"
|
||||
:placeholder="placeholder || ''"
|
||||
@keyup="doCheck"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
<span class="icon is-left">
|
||||
@@ -14,11 +15,20 @@
|
||||
:size="22"
|
||||
/>
|
||||
</span>
|
||||
</p>
|
||||
<p
|
||||
v-if="unit"
|
||||
class="control"
|
||||
>
|
||||
<a class="button is-static fs-12 h-full">
|
||||
{{ unit }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ["record", "attr", "placeholder", "disabled", "defaultValue"],
|
||||
props: ["record", "attr", "placeholder", "disabled", "defaultValue", "unit"],
|
||||
data() {
|
||||
return {
|
||||
value: this.getInitialValue(),
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:add-2-rounded"
|
||||
name="material-symbols:add-rounded"
|
||||
:size="22"
|
||||
/>
|
||||
</span>
|
||||
@@ -210,7 +210,7 @@
|
||||
>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:settings-outline-rounded"
|
||||
name="material-symbols:table-edit-outline"
|
||||
:size="22"
|
||||
/>
|
||||
</span>
|
||||
@@ -240,8 +240,8 @@
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
<div class="tabs is-toggle">
|
||||
<ul class="is-flex-grow-0 mx-auto">
|
||||
<div class="tabs is-toggle is-centered">
|
||||
<ul>
|
||||
<li
|
||||
v-for="(v, i) in getMenu().filter((x) =>
|
||||
currentField.format === 'number'
|
||||
@@ -254,32 +254,31 @@
|
||||
:class="selectTab.code === v.code ? 'is-active' : 'has-text-primary'"
|
||||
@click="changeTab(v)"
|
||||
>
|
||||
<a class="px-4 py-1.5">{{ v.name }}</a>
|
||||
<a class="px-8 py-1.5 fs-14">{{ v.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="currentTab === 'detail'">
|
||||
<p class="fs-14 mt-3">
|
||||
<strong> Tên trường: </strong> {{ currentField.name }}
|
||||
<a @click="copyContent(currentField.name)">
|
||||
<span class="tooltip">
|
||||
<SvgIcon
|
||||
class="ml-1"
|
||||
v-bind="{ name: 'copy.svg', type: 'primary', size: 16 }"
|
||||
></SvgIcon>
|
||||
<span
|
||||
class="tooltiptext"
|
||||
style="top: 110%; bottom: unset; min-width: max-content; left: 25px"
|
||||
>Copy</span
|
||||
<div class="fs-14 mt-3 is-flex is-gap-0.5 is-align-items-center">
|
||||
<p class="font-semibold">Tên trường:</p>
|
||||
<p>{{ currentField.name }}</p>
|
||||
<button
|
||||
class="button is-white is-small"
|
||||
@click="copyContent(currentField.name)"
|
||||
>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:content-copy-outline-rounded"
|
||||
:size="16"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
<label class="label fs-14 mt-3">Mô tả<span class="has-text-danger"> *</span></label>
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<input
|
||||
class="input fs-14"
|
||||
class="input fs-14 h-full"
|
||||
type="text"
|
||||
@change="changeLabel($event.target.value)"
|
||||
v-model="label"
|
||||
@@ -287,39 +286,48 @@
|
||||
</div>
|
||||
<div class="control">
|
||||
<button
|
||||
class="button"
|
||||
class="button h-full"
|
||||
@click="editLabel()"
|
||||
>
|
||||
<SvgIcon v-bind="{ name: 'pen.svg', type: 'dark', size: 19 }"></SvgIcon>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:edit-outline-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
class="help is-danger"
|
||||
v-if="errors.find((v) => v.name === 'label')"
|
||||
class="help is-danger"
|
||||
>
|
||||
{{ errors.find((v) => v.name === "label").msg }}
|
||||
</p>
|
||||
<div class="field mt-3">
|
||||
<label class="label fs-14">Kiểu dữ liệu<span class="has-text-danger"> * </span></label>
|
||||
<div class="control fs-14">
|
||||
<span
|
||||
class="mr-4"
|
||||
v-for="(v, i) in datatype"
|
||||
<button
|
||||
v-for="v in datatype"
|
||||
class="button is-white fs-14"
|
||||
>
|
||||
<span
|
||||
class="icon-text"
|
||||
v-if="radioType === v"
|
||||
>
|
||||
<SvgIcon v-bind="{ name: 'radio-checked.svg', type: 'gray', size: 22 }"></SvgIcon>
|
||||
</span>
|
||||
{{ v.name }}
|
||||
<span class="icon">
|
||||
<Icon
|
||||
:name="
|
||||
radioType === v
|
||||
? 'material-symbols:radio-button-checked-outline'
|
||||
: 'material-symbols:radio-button-unchecked'
|
||||
"
|
||||
:size="22"
|
||||
/>
|
||||
</span>
|
||||
<span>{{ v.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="field is-horizontal"
|
||||
v-if="currentField.format === 'number'"
|
||||
class="field is-horizontal"
|
||||
>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
@@ -334,7 +342,7 @@
|
||||
position: 'is-top-left',
|
||||
}"
|
||||
@option="selected('_account', $event)"
|
||||
></SearchBox>
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
class="help has-text-danger"
|
||||
@@ -361,27 +369,25 @@
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<label class="label fs-14">Định dạng nâng cao</label>
|
||||
<p class="control fs-14">
|
||||
<span
|
||||
class="mr-4"
|
||||
v-for="(v, i) in colorchoice.filter((v) => v.code !== 'condition')"
|
||||
>
|
||||
<a
|
||||
class="icon-text"
|
||||
<div class="control">
|
||||
<button
|
||||
v-for="v in colorchoice.filter((v) => v.code !== 'condition')"
|
||||
@click="changeTemplate(v)"
|
||||
class="button is-white fs-14"
|
||||
>
|
||||
<SvgIcon
|
||||
v-bind="{
|
||||
name: `radio-${radioTemplate === v.code ? '' : 'un'}checked.svg`,
|
||||
type: 'gray',
|
||||
size: 22,
|
||||
}"
|
||||
>
|
||||
</SvgIcon>
|
||||
</a>
|
||||
{{ v.name }}
|
||||
<span class="icon">
|
||||
<Icon
|
||||
:name="
|
||||
radioTemplate === v.code
|
||||
? 'material-symbols:radio-button-checked-outline'
|
||||
: 'material-symbols:radio-button-unchecked'
|
||||
"
|
||||
:size="22"
|
||||
/>
|
||||
</span>
|
||||
</p>
|
||||
<span>{{ v.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -390,10 +396,10 @@
|
||||
v-if="radioTemplate === 'option'"
|
||||
>
|
||||
<button
|
||||
class="button is-primary is-small has-text-white"
|
||||
class="button is-primary is-light fs-14"
|
||||
@click="showSidebar()"
|
||||
>
|
||||
<span class="fs-14">{{ `${currentField.template ? "Sửa" : "Tạo"} định dạng` }}</span>
|
||||
{{ `${currentField.template ? "Sửa" : "Tạo"} định dạng` }}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
@@ -414,7 +420,7 @@
|
||||
@label="changeLabel"
|
||||
@updatefields="updateFields"
|
||||
@close="close"
|
||||
></Modal>
|
||||
/>
|
||||
</template>
|
||||
<script setup>
|
||||
import { useStore } from "@/stores/index";
|
||||
@@ -522,7 +528,6 @@ function doRemove() {
|
||||
copy.fields.splice(idx, 1);
|
||||
copy.update = { fields: copy.fields };
|
||||
store.commit(props.pagename, copy);
|
||||
emit("close");
|
||||
}
|
||||
function fieldList() {
|
||||
showmodal.value = {
|
||||
@@ -538,7 +543,7 @@ function tableOption() {
|
||||
component: "datatable/TableSetting",
|
||||
vbind: { pagename: props.pagename },
|
||||
title: "Tùy chọn bảng",
|
||||
width: "40%",
|
||||
width: "50%",
|
||||
height: "400px",
|
||||
};
|
||||
}
|
||||
@@ -556,9 +561,10 @@ const changeLabel = function (text) {
|
||||
function editLabel() {
|
||||
showmodal.value = {
|
||||
component: "datatable/EditLabel",
|
||||
title: "Điều chỉnh tiêu đề",
|
||||
width: "500px",
|
||||
height: "300px",
|
||||
vbind: { label: label },
|
||||
vbind: { label },
|
||||
};
|
||||
}
|
||||
const changeTemplate = function (v) {
|
||||
@@ -598,7 +604,7 @@ const saveSetting = function () {
|
||||
vbind: { pagename: props.pagename, classify: 4 },
|
||||
title: "Lưu thiết lập",
|
||||
width: "500px",
|
||||
height: "400px",
|
||||
height: "auto",
|
||||
};
|
||||
};
|
||||
const showSidebar = function () {
|
||||
|
||||
@@ -4,33 +4,41 @@
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<label class="label">Đối tượng</label>
|
||||
<p class="control fs-14">
|
||||
<b-radio
|
||||
<div class="control fs-14 is-flex is-gap-2">
|
||||
<label
|
||||
v-for="(v, i) in types"
|
||||
:key="i"
|
||||
v-model="type"
|
||||
:native-value="v"
|
||||
@input="changeType(v)"
|
||||
class="radio"
|
||||
>
|
||||
<input
|
||||
v-model="type"
|
||||
@input="changeType(v)"
|
||||
type="radio"
|
||||
name="type"
|
||||
/>
|
||||
{{ v.name }}
|
||||
</b-radio>
|
||||
</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Kích cỡ</label>
|
||||
<p class="control fs-14">
|
||||
<b-radio
|
||||
<div class="control fs-14 is-flex is-gap-2">
|
||||
<label
|
||||
v-for="(v, i) in sizes.filter((v) =>
|
||||
type ? (type.code === 'tag' ? v.code !== 'is-small' : 1 > 0) : true,
|
||||
)"
|
||||
:key="i"
|
||||
v-model="size"
|
||||
:native-value="v"
|
||||
@input="changeType(v)"
|
||||
class="radio"
|
||||
>
|
||||
<input
|
||||
v-model="size"
|
||||
@input="changeType(v)"
|
||||
type="radio"
|
||||
name="size"
|
||||
/>
|
||||
{{ v.name }}
|
||||
</b-radio>
|
||||
</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label
|
||||
@@ -38,20 +46,21 @@
|
||||
v-if="['tag'].find((v) => v === type.code)"
|
||||
>Hình khối</label
|
||||
>
|
||||
<p
|
||||
class="control fs-14"
|
||||
v-if="['tag'].find((v) => v === type.code)"
|
||||
>
|
||||
<b-radio
|
||||
<div class="control fs-14 is-flex is-gap-2">
|
||||
<label
|
||||
v-for="(v, i) in shapes"
|
||||
:key="i"
|
||||
v-model="shape"
|
||||
:native-value="v"
|
||||
@input="changeType(v)"
|
||||
class="radio"
|
||||
>
|
||||
<input
|
||||
v-model="shape"
|
||||
@input="changeType(v)"
|
||||
type="radio"
|
||||
name="shape"
|
||||
/>
|
||||
{{ v.name }}
|
||||
</b-radio>
|
||||
</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -116,7 +125,7 @@
|
||||
:key="i"
|
||||
@click="changeTab(v)"
|
||||
>
|
||||
<a class="fs-15">{{ v.name }}</a>
|
||||
<a class="">{{ v.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -346,68 +355,77 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="tab.code === 'template'">
|
||||
<p class="mb-3">
|
||||
<a
|
||||
<div class="buttons mb-3">
|
||||
<button
|
||||
@click="copyContent()"
|
||||
class="mr-6"
|
||||
class="button is-primary is-light fs-14"
|
||||
>
|
||||
<span class="icon-text">
|
||||
<SvgIcon
|
||||
class="mr-2"
|
||||
v-bind="{ name: 'copy.svg', type: 'primary', siz: 18 }"
|
||||
></SvgIcon>
|
||||
<span class="fs-16">Copy</span>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:content-copy-outline-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
<span>Copy</span>
|
||||
</button>
|
||||
<button
|
||||
@click="paste()"
|
||||
class="mr-6"
|
||||
class="button is-primary is-light fs-14"
|
||||
>
|
||||
<span class="icon-text">
|
||||
<SvgIcon
|
||||
class="mr-2"
|
||||
v-bind="{ name: 'pen1.svg', type: 'primary', siz: 18 }"
|
||||
></SvgIcon>
|
||||
<span class="fs-16">Paste</span>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:content-paste-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
<span>Paste</span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<textarea
|
||||
class="textarea fs-14"
|
||||
rows="8"
|
||||
class="textarea fs-13"
|
||||
style="font-family: monospace"
|
||||
rows="4"
|
||||
v-model="text"
|
||||
@dblclick="doCheck"
|
||||
></textarea>
|
||||
</div>
|
||||
<p class="mt-5">
|
||||
<span class="icon-text fsb-18">
|
||||
Replace
|
||||
<SvgIcon v-bind="{ name: 'right.svg', type: 'dark', size: 22 }"></SvgIcon>
|
||||
<span class="icon-text">
|
||||
<span class="fs-17 font-semibold">Replace</span>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:arrow-forward-ios-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
</p>
|
||||
<div class="field is-grouped mt-4">
|
||||
<div class="control">
|
||||
<p class="fsb-14 mb-1">Đoạn text</p>
|
||||
<label class="label">Đoạn text</label>
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder=""
|
||||
v-model="source"
|
||||
/>
|
||||
</div>
|
||||
<div class="control">
|
||||
<p class="fsb-14 mb-1">Thay bằng</p>
|
||||
<label class="label">Thay bằng</label>
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder=""
|
||||
v-model="target"
|
||||
/>
|
||||
</div>
|
||||
<div class="control pl-5">
|
||||
<div class="control">
|
||||
<label
|
||||
class="label"
|
||||
style="opacity: 0"
|
||||
>Hidden</label
|
||||
>
|
||||
<button
|
||||
class="button is-primary is-outlined mt-5"
|
||||
class="button is-primary is-light"
|
||||
@click="replace()"
|
||||
>
|
||||
Replace
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
<template>
|
||||
<div
|
||||
class="field is-grouped is-grouped-multiline pl-2"
|
||||
class="field is-grouped is-grouped-multiline is-gap-4 px-2"
|
||||
v-if="filters ? filters.length > 0 : false"
|
||||
>
|
||||
<div class="control mr-5">
|
||||
<div class="control">
|
||||
<a
|
||||
class="button is-primary is-small has-text-white has-text-weight-bold"
|
||||
class="button is-primary is-light"
|
||||
@click="updateData({ filters: [] })"
|
||||
>
|
||||
<span class="fs-14">Xóa lọc</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="control pr-2 mr-5">
|
||||
<div class="control">
|
||||
<span class="icon-text">
|
||||
<SvgIcon v-bind="{ name: 'sigma.svg', type: 'primary', size: 20 }"></SvgIcon>
|
||||
<span class="fsb-18 has-text-primary">{{ totalRows }}</span>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="mdi:sigma"
|
||||
:size="20"
|
||||
class="has-text-primary"
|
||||
/>
|
||||
</span>
|
||||
<span class="fs-16 font-semibold has-text-primary">{{ totalRows }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
@@ -24,16 +30,16 @@
|
||||
>
|
||||
<div class="tags has-addons is-marginless">
|
||||
<a
|
||||
class="tag is-primary has-text-white is-marginless"
|
||||
class="tag is-primary is-light is-marginless"
|
||||
@click="showCondition(v)"
|
||||
>{{ v.label.indexOf(">") >= 0 ? $stripHtml(v.label, 30) : v.label }}</a
|
||||
>
|
||||
<a
|
||||
class="tag is-delete is-marginless has-text-black-bis"
|
||||
class="tag is-delete is-marginless"
|
||||
@click="removeFilter(i)"
|
||||
></a>
|
||||
</div>
|
||||
<span class="help has-text-black-bis">
|
||||
<span class="help">
|
||||
{{
|
||||
v.sort
|
||||
? v.sort
|
||||
@@ -59,14 +65,13 @@
|
||||
v-for="(field, i) in displayFields"
|
||||
:key="i"
|
||||
:style="field.headerStyle"
|
||||
>
|
||||
<div
|
||||
class="is-clickable header"
|
||||
@click="showField(field)"
|
||||
:style="field.dropStyle"
|
||||
>
|
||||
<div :style="field.dropStyle">
|
||||
<a
|
||||
v-if="field.label.indexOf('<') < 0"
|
||||
class="has-text-white font-semibold"
|
||||
class="font-semibold"
|
||||
>{{ field.label }}</a
|
||||
>
|
||||
<a v-else>
|
||||
@@ -160,13 +165,12 @@ var currentPage = 1;
|
||||
var displayFields = ref([]);
|
||||
var displayData = [];
|
||||
var pagedata = store[props.pagename];
|
||||
console.log("props.pagename", props.pagename);
|
||||
console.log("store", toRaw(store));
|
||||
console.log("store[props.pagename]", toRaw(store[props.pagename]));
|
||||
console.log("pagedata.tablesetting", toRaw(pagedata.tablesetting)); // table-font-size is 13
|
||||
console.log("store.tablesetting", toRaw(store.tablesetting)); // table-font-size is 12
|
||||
// var tablesetting = $copy(pagedata.tablesetting || store.tablesetting);
|
||||
var tablesetting = $copy(store.tablesetting);
|
||||
var tablesetting = $copy(pagedata.tablesetting || store.tablesetting);
|
||||
if (!Array.isArray(tablesetting)) {
|
||||
tablesetting = Object.values(tablesetting);
|
||||
}
|
||||
// var tablesetting = $copy(store.tablesetting);
|
||||
// const tablesettingObj = Object.fromEntries(tablesetting.map((v) => [v.code, v]));
|
||||
var perPage = Number($find(tablesetting, { code: "per-page" }, "detail")) || 20;
|
||||
var filters = $copy(pagedata.filters || []);
|
||||
var currentField;
|
||||
@@ -551,7 +555,10 @@ setTimeout(() => updateShow(), 200);
|
||||
</script>
|
||||
<style scoped>
|
||||
:deep(.table tbody tr:hover td, .table tbody tr:hover th) {
|
||||
background-color: hsl(0, 0%, 78%);
|
||||
color: rgb(0, 0, 0);
|
||||
--bulma-table-row-hover-background-color: var(--bulma-scheme-main-ter);
|
||||
background-color: var(--bulma-table-row-hover-background-color);
|
||||
}
|
||||
.header:hover a {
|
||||
opacity: 0.75;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<p class="fsb-20 mb-5">Điều chỉnh tiêu đề</p>
|
||||
<div
|
||||
v-for="(v, i) in arr"
|
||||
:key="i"
|
||||
:class="i > 0 ? 'mt-4' : null"
|
||||
:class="i > 0 && 'mt-4'"
|
||||
>
|
||||
<p class="fsb-14">Dòng thứ {{ i + 1 }}<span class="has-text-danger"> *</span></p>
|
||||
<p class="font-semibold">Dòng thứ {{ i + 1 }}<span class="has-text-danger"> *</span></p>
|
||||
<div class="field has-addons mt-1">
|
||||
<div class="control is-expanded">
|
||||
<input
|
||||
@@ -16,23 +15,31 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a
|
||||
class="button px-2 is-primary"
|
||||
<button
|
||||
class="button is-success is-light"
|
||||
@click="add()"
|
||||
>
|
||||
<span> <SvgIcon v-bind="{ name: 'add1.png', type: 'white', size: 17 }"></SvgIcon></span>
|
||||
</a>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:add-rounded"
|
||||
:size="20"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="control"
|
||||
@click="remove(i)"
|
||||
v-if="i > 0"
|
||||
>
|
||||
<a class="button px-2 is-dark">
|
||||
<span>
|
||||
<SvgIcon v-bind="{ name: 'bin.svg', type: 'white', size: 17 }"></SvgIcon>
|
||||
<button class="button is-danger is-light">
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:delete-outline-rounded"
|
||||
:size="20"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
@@ -50,10 +57,10 @@
|
||||
Cập nhật
|
||||
</button>
|
||||
<button
|
||||
class="button is-dark"
|
||||
class="button is-white"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
Hủy bỏ
|
||||
Hủy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,7 +79,7 @@ export default {
|
||||
if (!this.$empty(v)) {
|
||||
let label = v + "</p>";
|
||||
label = this.$stripHtml(label);
|
||||
this.arr.push({ label: label });
|
||||
this.arr.push({ label });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -27,45 +27,56 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-narrow">
|
||||
<div class="field is-narrow has-addons">
|
||||
<p class="control">
|
||||
<a @click="addAttr()">
|
||||
<SvgIcon v-bind="{ name: 'add1.png', type: 'gray', size: 18 }"></SvgIcon>
|
||||
</a>
|
||||
<a
|
||||
class="ml-2"
|
||||
@click="remove(i)"
|
||||
>
|
||||
<SvgIcon v-bind="{ name: 'bin1.svg', type: 'gray', size: 18 }"></SvgIcon>
|
||||
</a>
|
||||
<a
|
||||
class="ml-2"
|
||||
<button
|
||||
class="button is-primary is-light"
|
||||
@click="jsonData(v, i)"
|
||||
>
|
||||
<SvgIcon v-bind="{ name: 'apps.svg', type: 'gray', size: 18 }"></SvgIcon>
|
||||
</a>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:app-registration-rounded"
|
||||
:size="20"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
<p class="control">
|
||||
<button
|
||||
class="button is-primary is-light"
|
||||
@click="remove(i)"
|
||||
>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:delete-outline-rounded"
|
||||
:size="20"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div
|
||||
class="mb-6"
|
||||
v-else
|
||||
>
|
||||
<button
|
||||
class="button is-primary has-text-white"
|
||||
class="button is-success is-light"
|
||||
@click="addAttr()"
|
||||
>
|
||||
Thêm thuộc tính
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:add-rounded"
|
||||
:size="20"
|
||||
/>
|
||||
</span>
|
||||
<span>Thêm thuộc tính</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="buttons mt-5">
|
||||
<a
|
||||
class="button is-primary has-text-white"
|
||||
<div class="mt-2">
|
||||
<button
|
||||
class="button is-primary"
|
||||
@click="update()"
|
||||
>Cập nhật</a
|
||||
>
|
||||
Cập nhật
|
||||
</button>
|
||||
</div>
|
||||
<Modal
|
||||
@close="comp = undefined"
|
||||
|
||||
@@ -1,55 +1,61 @@
|
||||
<template>
|
||||
<!-- v-if="currentsetting ? currentsetting.user === login.id : false" -->
|
||||
<div
|
||||
class="mb-4"
|
||||
v-if="currentsetting ? currentsetting.user === login.id : false"
|
||||
v-if="currentsetting"
|
||||
>
|
||||
<p class="fs-16 has-text-findata">
|
||||
<p>
|
||||
Đang mở: <b>{{ $stripHtml(currentsetting.name, 40) }}</b>
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label fs-14">Chọn chế độ lưu <span class="has-text-danger"> * </span></label>
|
||||
<label class="label">Chọn chế độ lưu <span class="has-text-danger"> * </span></label>
|
||||
<div class="control is-expanded fs-14">
|
||||
<a
|
||||
class="mr-5"
|
||||
<button
|
||||
class="button is-white has-text-inherit"
|
||||
v-if="isOverwrite()"
|
||||
@click="changeType('overwrite')"
|
||||
>
|
||||
<span class="icon-text">
|
||||
<SvgIcon
|
||||
v-bind="{
|
||||
name: radioSave === 'overwrite' ? 'radio-checked.svg' : 'radio-unchecked.svg',
|
||||
type: 'gray',
|
||||
size: 22,
|
||||
}"
|
||||
></SvgIcon>
|
||||
Ghi đè
|
||||
<span class="icon">
|
||||
<Icon
|
||||
:name="
|
||||
radioSave === 'overwrite'
|
||||
? 'material-symbols:radio-button-checked-outline'
|
||||
: 'material-symbols:radio-button-unchecked'
|
||||
"
|
||||
:size="22"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
<a @click="changeType('new')">
|
||||
<span class="icon-text">
|
||||
<SvgIcon
|
||||
v-bind="{
|
||||
name: radioSave === 'new' ? 'radio-checked.svg' : 'radio-unchecked.svg',
|
||||
type: 'gray',
|
||||
size: 22,
|
||||
}"
|
||||
></SvgIcon>
|
||||
Tạo mới
|
||||
<span>Ghi đè</span>
|
||||
</button>
|
||||
<button
|
||||
class="button is-white"
|
||||
@click="changeType('new')"
|
||||
>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
:name="
|
||||
radioSave === 'new'
|
||||
? 'material-symbols:radio-button-checked-outline'
|
||||
: 'material-symbols:radio-button-unchecked'
|
||||
"
|
||||
:size="22"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
<span>Tạo mới</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="radioSave === 'new'">
|
||||
<div class="field mt-4 px-0 mx-0">
|
||||
<label class="label fs-14">Tên thiết lập <span class="has-text-danger"> * </span></label>
|
||||
<div class="field">
|
||||
<label class="label">Tên thiết lập <span class="has-text-danger"> * </span></label>
|
||||
<div class="control">
|
||||
<!-- ref="name" -->
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder=""
|
||||
v-model="name"
|
||||
ref="name"
|
||||
v-on:keyup.enter="saveSetting"
|
||||
/>
|
||||
</div>
|
||||
@@ -60,12 +66,12 @@
|
||||
{{ errors.find((v) => v.name === "name").msg }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="field mt-4 px-0 mx-0">
|
||||
<label class="label fs-14"> Mô tả </label>
|
||||
<div class="field">
|
||||
<label class="label"> Mô tả </label>
|
||||
<p class="control is-expanded">
|
||||
<textarea
|
||||
class="textarea"
|
||||
rows="4"
|
||||
rows="3"
|
||||
v-model="note"
|
||||
></textarea>
|
||||
</p>
|
||||
@@ -93,11 +99,12 @@
|
||||
{{ status ? "Lưu thiết lập thành công." : "Lỗi. Lưu thiết lập thất bại." }}
|
||||
</label>
|
||||
<p class="control is-expanded">
|
||||
<a
|
||||
class="button is-primary has-text-white"
|
||||
<button
|
||||
:class="['button is-primary', isLoading && 'is-loading']"
|
||||
@click="saveSetting()"
|
||||
>Lưu lại</a
|
||||
>
|
||||
Lưu lại
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -106,28 +113,30 @@ import { ref } from "vue";
|
||||
import { useStore } from "@/stores/index";
|
||||
const emit = defineEmits([]);
|
||||
const store = useStore();
|
||||
var props = defineProps({
|
||||
const props = defineProps({
|
||||
pagename: String,
|
||||
classify: String,
|
||||
option: String,
|
||||
data: Object,
|
||||
focus: Boolean,
|
||||
});
|
||||
const { $empty, $copy, $filter, $stripHtml, $updateapi, $insertapi, $findIndex, $snackbar } = useNuxtApp();
|
||||
var pagename = props.pagename;
|
||||
var radioOption = ref();
|
||||
const { $empty, $copy, $stripHtml, $updateapi, $insertapi, $findIndex, $snackbar } = useNuxtApp();
|
||||
const radioOption = ref();
|
||||
var login = { id: 1 };
|
||||
var errors = [];
|
||||
var radioType = undefined;
|
||||
var radioDefault = 0;
|
||||
var radioSave = ref("new");
|
||||
var note = undefined;
|
||||
var status = undefined;
|
||||
var name = undefined;
|
||||
const errors = ref([]);
|
||||
const radioType = ref();
|
||||
const radioDefault = 0;
|
||||
const radioSave = ref("overwrite");
|
||||
const name = ref();
|
||||
const note = ref();
|
||||
const status = undefined;
|
||||
var currentsetting = undefined;
|
||||
var pagename = props.pagename;
|
||||
var pagedata = store[props.pagename];
|
||||
const isLoading = ref(false);
|
||||
|
||||
async function saveSetting() {
|
||||
errors = [];
|
||||
errors.value = [];
|
||||
let detail = pagename ? { fields: pagedata.fields } : {};
|
||||
if (pagename) {
|
||||
let element = pagedata.tablesetting || {};
|
||||
@@ -139,19 +148,21 @@ async function saveSetting() {
|
||||
if (props.option) detail.option = props.option;
|
||||
if (props.data) detail.data = props.data;
|
||||
let data = {
|
||||
user: login.id,
|
||||
name: name,
|
||||
detail: detail,
|
||||
note: note,
|
||||
type: radioType.id,
|
||||
// user: login.id,
|
||||
user: undefined,
|
||||
name: name.value,
|
||||
detail,
|
||||
note,
|
||||
type: radioType.value.id,
|
||||
classify: props.classify ? props.classify : store.settingclass.find((v) => v.code === "data-field").id,
|
||||
default: radioDefault,
|
||||
update_time: new Date(),
|
||||
};
|
||||
let result;
|
||||
isLoading.value = true;
|
||||
if (radioSave.value === "new") {
|
||||
if ($empty(name)) {
|
||||
return errors.push({
|
||||
if ($empty(name.value)) {
|
||||
return errors.value.push({
|
||||
name: "name",
|
||||
msg: "Tên thiết lập không được bỏ trống",
|
||||
});
|
||||
@@ -163,6 +174,7 @@ async function saveSetting() {
|
||||
copy.update_time = new Date();
|
||||
result = await $updateapi("usersetting", copy);
|
||||
}
|
||||
isLoading.value = false;
|
||||
if (radioSave.value === "new") {
|
||||
emit("modalevent", { name: "opensetting", data: result });
|
||||
} else {
|
||||
@@ -187,11 +199,12 @@ function changeOption(v) {
|
||||
radioOption.value = v.code;
|
||||
}
|
||||
function initData() {
|
||||
radioType = store.settingtype.find((v) => v.code === "private");
|
||||
radioType.value = store.settingtype.find((v) => v.code === "private");
|
||||
if (props.pagename) currentsetting = $copy(pagedata.setting ? pagedata.setting : undefined);
|
||||
if (!currentsetting) radioSave.value = "new";
|
||||
// disable temp: for now, radioSave is always 'overwrite'
|
||||
/* if (!currentsetting) radioSave.value = "new";
|
||||
else if (currentsetting.user !== login.id) radioSave.value = "new";
|
||||
else radioSave.value = "overwrite";
|
||||
else radioSave.value = "overwrite"; */
|
||||
}
|
||||
initData();
|
||||
</script>
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
<div class="tabs is-boxed">
|
||||
<ul>
|
||||
<li
|
||||
:class="selectType.code === v.code ? 'is-active fs-16' : 'fs-16'"
|
||||
v-for="v in fieldType"
|
||||
:class="selectType.code === v.code && 'is-active'"
|
||||
>
|
||||
<a @click="selectType = v"
|
||||
<a
|
||||
class="has-text-inherit"
|
||||
@click="selectType = v"
|
||||
><span>{{ v.name }}</span></a
|
||||
>
|
||||
</li>
|
||||
@@ -148,7 +150,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="field px-0 mx-0">
|
||||
<div class="field">
|
||||
<label class="label">Tên trường <span class="has-text-danger"> * </span> </label>
|
||||
<p class="control">
|
||||
<input
|
||||
@@ -166,13 +168,13 @@
|
||||
{{ errors.find((v) => v.name === "name").message }}
|
||||
</p>
|
||||
<p
|
||||
class="help has-text-primary"
|
||||
class="help has-text-grey"
|
||||
v-else
|
||||
>
|
||||
Tên trường do hệ thống tự sinh.
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-5">
|
||||
<div class="field">
|
||||
<label class="label">Mô tả<span class="has-text-danger"> *</span></label>
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
@@ -187,7 +189,12 @@
|
||||
class="button"
|
||||
@click="editLabel()"
|
||||
>
|
||||
<span><SvgIcon v-bind="{ name: 'pen.svg', type: 'dark', size: 17 }"></SvgIcon></span>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:edit-outline-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -199,53 +206,47 @@
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="field mt-5"
|
||||
class="field"
|
||||
v-if="selectType.code === 'empty'"
|
||||
>
|
||||
<label class="label"
|
||||
>Kiểu dữ liệu
|
||||
<span class="has-text-danger"> * </span>
|
||||
</label>
|
||||
<div class="control fs-14">
|
||||
<span
|
||||
class="mr-4"
|
||||
<div class="control">
|
||||
<button
|
||||
v-for="(v, i) in datatype"
|
||||
>
|
||||
<a
|
||||
class="icon-text"
|
||||
class="button is-white fs-14"
|
||||
@click="changeType(v)"
|
||||
>
|
||||
<SvgIcon
|
||||
v-bind="{
|
||||
name: `radio-${radioType.code === v.code ? '' : 'un'}checked.svg`,
|
||||
type: 'gray',
|
||||
size: 22,
|
||||
}"
|
||||
></SvgIcon>
|
||||
</a>
|
||||
{{ v.name }}
|
||||
<span class="icon">
|
||||
<Icon
|
||||
:name="
|
||||
radioType.code === v.code
|
||||
? 'material-symbols:radio-button-checked-outline'
|
||||
: 'material-symbols:radio-button-unchecked'
|
||||
"
|
||||
:size="22"
|
||||
/>
|
||||
</span>
|
||||
<span>{{ v.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field mt-5">
|
||||
<p class="control">
|
||||
<a
|
||||
class="button is-primary has-text-white"
|
||||
<button
|
||||
class="button is-primary"
|
||||
@click="selectType.code === 'formula' ? createField() : createEmptyField()"
|
||||
>Tạo cột</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
Tạo cột
|
||||
</button>
|
||||
<Modal
|
||||
v-bind="showmodal"
|
||||
v-if="showmodal"
|
||||
@label="changeLabel"
|
||||
@close="close"
|
||||
></Modal>
|
||||
/>
|
||||
</template>
|
||||
<script setup>
|
||||
import { useStore } from "@/stores/index";
|
||||
import ScrollBox from "~/components/datatable/ScrollBox";
|
||||
const emit = defineEmits(["modalevent"]);
|
||||
const store = useStore();
|
||||
const { $id, $copy, $clone, $empty, $stripHtml, $createField, $calc, $isNumber } = useNuxtApp();
|
||||
@@ -256,7 +257,7 @@ var props = defineProps({
|
||||
filterData: Object,
|
||||
width: String,
|
||||
});
|
||||
const moneyunit = store.moneyunit;
|
||||
const moneyunit = store.moneyunit || [];
|
||||
const datatype = store.datatype;
|
||||
var showmodal = ref();
|
||||
var pagedata = store[props.pagename];
|
||||
@@ -265,16 +266,15 @@ var data = [];
|
||||
var current = 1;
|
||||
var filterData = [];
|
||||
var loading = false;
|
||||
var fieldType = [
|
||||
const fieldType = [
|
||||
{ code: "formula", name: "Tạo công thức" },
|
||||
{ code: "empty", name: "Tạo cột rỗng" },
|
||||
];
|
||||
var errors = [];
|
||||
var tags = [];
|
||||
var formula = undefined;
|
||||
var name = `f${$id().toLocaleLowerCase()}`;
|
||||
var label = undefined;
|
||||
var errors = [];
|
||||
const errors = ref([]);
|
||||
var selectType = fieldType.find((v) => v.code === "empty");
|
||||
var radioType = ref(datatype.find((v) => v.code === "string"));
|
||||
var fields = [];
|
||||
@@ -362,25 +362,25 @@ function checkFunc() {
|
||||
return error ? "error" : content;
|
||||
}
|
||||
function checkValid() {
|
||||
errors = [];
|
||||
errors.value = [];
|
||||
if (tags.length === 0 && choice === "column") {
|
||||
errors.push({
|
||||
errors.value.push({
|
||||
name: "tags",
|
||||
message: "Chưa chọn trường xây dựng công thức.",
|
||||
});
|
||||
}
|
||||
if (!$empty(formula) ? $empty(formula.trim()) : true) {
|
||||
errors.push({ name: "formula", message: "Công thức không được bỏ trống." });
|
||||
errors.value.push({ name: "formula", message: "Công thức không được bỏ trống." });
|
||||
}
|
||||
if (!$empty(label) ? $empty(label.trim()) : true)
|
||||
errors.push({ name: "label", message: "Mô tả không được bỏ trống." });
|
||||
errors.value.push({ name: "label", message: "Mô tả không được bỏ trống." });
|
||||
else if (pagedata.fields.find((v) => v.label.toLowerCase() === label.toLowerCase())) {
|
||||
errors.push({
|
||||
errors.value.push({
|
||||
name: "label",
|
||||
message: "Mô tả bị trùng. Hãy đặt mô tả khác.",
|
||||
});
|
||||
}
|
||||
if (errors.length > 0) return false;
|
||||
if (errors.value.length > 0) return false;
|
||||
//check formula in case use column
|
||||
if (choice === "column") {
|
||||
let val = $copy(formula);
|
||||
@@ -391,20 +391,20 @@ function checkValid() {
|
||||
try {
|
||||
let value = $calc(val);
|
||||
if (isNaN(value) || value === Number.POSITIVE_INFINITY || value === Number.NEGATIVE_INFINITY) {
|
||||
errors.push({ name: "formula", message: "Công thức không hợp lệ" });
|
||||
errors.value.push({ name: "formula", message: "Công thức không hợp lệ" });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
errors.push({ name: "formula", message: "Công thức không hợp lệ" });
|
||||
errors.value.push({ name: "formula", message: "Công thức không hợp lệ" });
|
||||
}
|
||||
} else {
|
||||
if (checkFunc() === "error")
|
||||
errors.push({
|
||||
errors.value.push({
|
||||
name: "formula",
|
||||
message: `Hàm ${func.toUpperCase()} không hợp lệ`,
|
||||
});
|
||||
}
|
||||
return errors.length > 0 ? false : true;
|
||||
return errors.value.length > 0 ? false : true;
|
||||
}
|
||||
function createField() {
|
||||
if (!checkValid()) return;
|
||||
@@ -429,23 +429,26 @@ function createField() {
|
||||
emit("close");
|
||||
}
|
||||
function createEmptyField() {
|
||||
errors = [];
|
||||
if (!$empty(name) ? $empty(name.trim()) : true) errors.push({ name: "name", message: "Tên không được bỏ trống." });
|
||||
console.log("createEmptyField");
|
||||
errors.value = [];
|
||||
if (!$empty(name) ? $empty(name.trim()) : true)
|
||||
errors.value.push({ name: "name", message: "Tên không được bỏ trống." });
|
||||
else if (pagedata.fields.find((v) => v.name.toLowerCase() === name.toLowerCase())) {
|
||||
errors.push({
|
||||
errors.value.push({
|
||||
name: "name",
|
||||
message: "Tên trường bị trùng. Hãy đặt tên khác.",
|
||||
});
|
||||
}
|
||||
if (!$empty(label) ? $empty(label.trim()) : true)
|
||||
errors.push({ name: "label", message: "Mô tả không được bỏ trống." });
|
||||
errors.value.push({ name: "label", message: "Mô tả không được bỏ trống." });
|
||||
else if (pagedata.fields.find((v) => v.label.toLowerCase() === label.toLowerCase())) {
|
||||
errors.push({
|
||||
errors.value.push({
|
||||
name: "label",
|
||||
message: "Mô tả bị trùng. Hãy đặt mô tả khác.",
|
||||
});
|
||||
}
|
||||
if (errors.length > 0) return;
|
||||
console.log("errors", errors);
|
||||
if (errors.value.length > 0) return;
|
||||
let field = $createField(name.trim(), label.trim(), radioType.value.code, true);
|
||||
if (selectType.code === "chart") field = createChartField();
|
||||
let copy = $clone(pagedata);
|
||||
@@ -453,6 +456,7 @@ function createEmptyField() {
|
||||
copy.update = { fields: copy.fields };
|
||||
store.commit(props.pagename, copy);
|
||||
//pagedata = copy
|
||||
console.log("field", field);
|
||||
emit("newfield", field);
|
||||
label = undefined;
|
||||
name = `f${$id()}`;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<table class="table">
|
||||
<table class="table is-fullwidth">
|
||||
<thead>
|
||||
<tr class="fs-14">
|
||||
<th>#</th>
|
||||
<th>Tên trường</th>
|
||||
<th>Tên cột</th>
|
||||
<th>...</th>
|
||||
<th class="is-narrow">...</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -23,21 +23,47 @@
|
||||
</td>
|
||||
<td>{{ $stripHtml(v.label, 50) }}</td>
|
||||
<td>
|
||||
<a
|
||||
class="mr-4"
|
||||
<div class="field has-addons">
|
||||
<p class="control">
|
||||
<button
|
||||
class="button is-primary is-light"
|
||||
@click="moveDown(v, i)"
|
||||
>
|
||||
<SvgIcon v-bind="{ name: 'down1.png', type: 'dark', size: 18 }"></SvgIcon>
|
||||
</a>
|
||||
<a
|
||||
class="mr-4"
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:arrow-downward-rounded"
|
||||
:size="19"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
<p class="control">
|
||||
<button
|
||||
class="button is-primary is-light"
|
||||
@click="moveUp(v, i)"
|
||||
>
|
||||
<SvgIcon v-bind="{ name: 'up.png', type: 'dark', size: 18 }"></SvgIcon>
|
||||
</a>
|
||||
<a @click="askConfirm(v, i)">
|
||||
<SvgIcon v-bind="{ name: 'bin1.svg', type: 'dark', size: 18 }"></SvgIcon>
|
||||
</a>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:arrow-upward-rounded"
|
||||
:size="19"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
<p class="control">
|
||||
<button
|
||||
class="button is-primary is-light"
|
||||
@click="askConfirm(v, i)"
|
||||
>
|
||||
<span class="icon">
|
||||
<Icon
|
||||
name="material-symbols:delete-outline-rounded"
|
||||
:size="19"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -1,33 +1,38 @@
|
||||
<template>
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<label class="label fs-14"> Cỡ chữ của bảng <span class="has-text-danger"> * </span></label>
|
||||
<div class="fixed-grid has-12-cols">
|
||||
<div class="grid">
|
||||
<div class="cell is-col-span-4">
|
||||
<div class="box">
|
||||
<label class="label fs-14">Cỡ chữ của bảng</label>
|
||||
<p class="control fs-14">
|
||||
<input
|
||||
class="input is-small"
|
||||
class="input fs-13"
|
||||
type="text"
|
||||
:value="tablesetting.find((v) => v.code === 'table-font-size').detail"
|
||||
@change="changeSetting($event.target.value, 'table-font-size')"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label fs-14"> Cỡ chữ tiêu đề <span class="has-text-danger"> * </span></label>
|
||||
<p class="control fs-14">
|
||||
</div>
|
||||
<div class="cell is-col-span-4">
|
||||
<div class="box">
|
||||
<label class="label fs-14">Cỡ chữ tiêu đề</label>
|
||||
<p class="control">
|
||||
<input
|
||||
class="input is-small"
|
||||
class="input fs-13"
|
||||
type="text"
|
||||
:value="tablesetting.find((v) => v.code === 'header-font-size').detail"
|
||||
@change="changeSetting($event.target.value, 'header-font-size')"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label fs-14"> Số dòng trên 1 trang <span class="has-text-danger"> * </span> </label>
|
||||
<p class="control fs-14">
|
||||
</div>
|
||||
<div class="cell is-col-span-4">
|
||||
<div class="box">
|
||||
<label class="label fs-14">Số dòng trên 1 trang</label>
|
||||
<p class="control">
|
||||
<input
|
||||
class="input is-small"
|
||||
class="input fs-13"
|
||||
type="text"
|
||||
:value="tablesetting.find((v) => v.code === 'per-page').detail"
|
||||
@change="changeSetting($event.target.value, 'per-page')"
|
||||
@@ -35,72 +40,115 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-horizontal mt-5">
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<label class="label fs-14"> Màu nền bảng <span class="has-text-danger"> * </span> </label>
|
||||
<p class="control fs-14">
|
||||
<div class="cell is-col-span-6">
|
||||
<div class="box">
|
||||
<label class="label fs-14">Màu nền bảng</label>
|
||||
<p class="control is-flex is-gap-1">
|
||||
<input
|
||||
type="color"
|
||||
:value="tablesetting.find((v) => v.code === 'table-background').detail"
|
||||
@change="changeSetting($event.target.value, 'table-background')"
|
||||
/>
|
||||
<input
|
||||
class="input fs-13"
|
||||
type="text"
|
||||
placeholder="#f29384, var(--bulma-blue)"
|
||||
:value="tablesetting.find((v) => v.code === 'table-background').detail"
|
||||
@change="changeSetting($event.target.value, 'table-background')"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label fs-14"> Màu chữ <span class="has-text-danger"> * </span> </label>
|
||||
<p class="control fs-14">
|
||||
</div>
|
||||
<div class="cell is-col-span-6">
|
||||
<div class="box">
|
||||
<label class="label fs-14">Màu chữ</label>
|
||||
<p class="control is-flex is-gap-1">
|
||||
<input
|
||||
type="color"
|
||||
:value="tablesetting.find((v) => v.code === 'table-font-color').detail"
|
||||
@change="changeSetting($event.target.value, 'table-font-color')"
|
||||
/>
|
||||
<input
|
||||
class="input fs-13"
|
||||
type="text"
|
||||
placeholder="#f29384, var(--bulma-blue)"
|
||||
:value="tablesetting.find((v) => v.code === 'table-font-color').detail"
|
||||
@change="changeSetting($event.target.value, 'table-font-color')"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-horizontal mt-5">
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<label class="label fs-14"> Màu chữ tiêu đề <span class="has-text-danger"> * </span> </label>
|
||||
<p class="control fs-14">
|
||||
<div class="cell is-col-span-6">
|
||||
<div class="box">
|
||||
<label class="label fs-14">Màu chữ tiêu đề</label>
|
||||
<p class="control is-flex is-gap-1">
|
||||
<input
|
||||
type="color"
|
||||
:value="tablesetting.find((v) => v.code === 'header-font-color').detail"
|
||||
@change="changeSetting($event.target.value, 'header-font-color')"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label fs-14"> Màu nền tiêu đề <span class="has-text-danger"> * </span> </label>
|
||||
<p class="control fs-14">
|
||||
<input
|
||||
type="color"
|
||||
:value="tablesetting.find((v) => v.code === 'header-background').detail"
|
||||
@change="changeSetting($event.target.value, 'header-background')"
|
||||
class="input fs-13"
|
||||
type="text"
|
||||
placeholder="#f29384, var(--bulma-blue)"
|
||||
:value="tablesetting.find((v) => v.code === 'header-font-color').detail"
|
||||
@change="changeSetting($event.target.value, 'header-font-color')"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label fs-14"> Màu chữ khi filter<span class="has-text-danger"> * </span> </label>
|
||||
<p class="control fs-14">
|
||||
</div>
|
||||
<div class="cell is-col-span-6">
|
||||
<div class="box">
|
||||
<label class="label fs-14">Màu nền tiêu đề</label>
|
||||
<div class="control is-flex is-gap-1 is-align-items-stretch">
|
||||
<input
|
||||
type="color"
|
||||
:value="rgbToHex(headerBg)"
|
||||
@change="changeSetting($event.target.value, 'header-background')"
|
||||
/>
|
||||
<input
|
||||
class="input fs-13 h-full"
|
||||
type="text"
|
||||
placeholder="#f29384, var(--bulma-blue)"
|
||||
:value="tablesetting.find((v) => v.code === 'header-background').detail"
|
||||
@change="changeSetting($event.target.value, 'header-background')"
|
||||
/>
|
||||
|
||||
<div
|
||||
ref="renderedHeaderBg"
|
||||
class="is-hidden"
|
||||
:style="{
|
||||
backgroundColor: tablesetting.find((v) => v.code === 'header-background').detail,
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell is-col-span-6">
|
||||
<div class="box">
|
||||
<label class="label fs-14">Màu chữ khi filter</label>
|
||||
<p class="control is-flex is-gap-1">
|
||||
<input
|
||||
type="color"
|
||||
:value="tablesetting.find((v) => v.code === 'header-filter-color').detail"
|
||||
@change="changeSetting($event.target.value, 'header-filter-color')"
|
||||
/>
|
||||
<input
|
||||
class="input fs-13"
|
||||
type="text"
|
||||
placeholder="#f29384, var(--bulma-blue)"
|
||||
:value="tablesetting.find((v) => v.code === 'header-filter-color').detail"
|
||||
@change="changeSetting($event.target.value, 'header-filter-color')"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-horizontal mt-5">
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<label class="label fs-14"> Đường viền <span class="has-text-danger"> * </span> </label>
|
||||
<div class="cell is-col-span-6">
|
||||
<div class="box">
|
||||
<label class="label fs-14">Đường viền</label>
|
||||
<p class="control fs-14">
|
||||
<input
|
||||
class="input is-small"
|
||||
class="input fs-13"
|
||||
type="text"
|
||||
:value="
|
||||
tablesetting.find((v) => v.code === 'td-border')
|
||||
@@ -113,20 +161,30 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="button is-primary"
|
||||
@click="emit('close')"
|
||||
>
|
||||
Cập nhật
|
||||
</button>
|
||||
</template>
|
||||
<script setup>
|
||||
import { useStore } from "@/stores/index";
|
||||
const store = useStore();
|
||||
var props = defineProps({
|
||||
const props = defineProps({
|
||||
pagename: String,
|
||||
});
|
||||
const { $copy, $clone, $empty } = useNuxtApp();
|
||||
var pagedata = $clone(store[props.pagename]);
|
||||
var errors = [];
|
||||
var radioNote = "no";
|
||||
const emit = defineEmits(["close"]);
|
||||
const { $copy, $clone, $empty, $store } = useNuxtApp();
|
||||
var pagedata = $clone($store[props.pagename]);
|
||||
if (!Array.isArray(pagedata.tablesetting)) {
|
||||
pagedata.tablesetting = Object.values(pagedata.tablesetting);
|
||||
}
|
||||
const errors = ref([]);
|
||||
let radioNote = "no";
|
||||
var tablesetting = pagedata.tablesetting;
|
||||
let found = tablesetting.find((v) => v.code === "note");
|
||||
if (found ? found.detail !== "@" : false) radioNote = "yes";
|
||||
|
||||
function changeSetting(value, code) {
|
||||
if (code === "note" && $empty(value)) return;
|
||||
let copy = $copy(tablesetting);
|
||||
@@ -139,6 +197,45 @@ function changeSetting(value, code) {
|
||||
}
|
||||
tablesetting = copy;
|
||||
pagedata.tablesetting = tablesetting;
|
||||
store.commit(props.pagename, pagedata);
|
||||
$store.commit(props.pagename, pagedata);
|
||||
}
|
||||
|
||||
const renderedHeaderBg = useTemplateRef("renderedHeaderBg");
|
||||
const headerBg = ref("#000000");
|
||||
|
||||
onMounted(() => {
|
||||
headerBg.value = window.getComputedStyle(renderedHeaderBg.value).backgroundColor;
|
||||
});
|
||||
watch();
|
||||
|
||||
function rgbToHex(rgb) {
|
||||
const [r, g, b] = rgb.match(/\d+/g);
|
||||
return "#" + [r, g, b].map((x) => Number(x).toString(16).padStart(2, "0")).join("");
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
input[type="color"] {
|
||||
width: var(--bulma-control-height);
|
||||
min-width: var(--bulma-control-height);
|
||||
max-width: var(--bulma-control-height);
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
input[type="color"]::-webkit-color-swatch-wrapper {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
input[type="color"]::-webkit-color-swatch {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.box {
|
||||
box-shadow: none;
|
||||
border: 1px solid var(--bulma-grey-90);
|
||||
background-color: var(--bulma-grey-95);
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,7 +20,8 @@ const body = ref({
|
||||
});
|
||||
|
||||
function selected(field, data) {
|
||||
body.value[field] = data.id;
|
||||
if (data === null) body.value[field] = data;
|
||||
else body.value[field] = data.id;
|
||||
}
|
||||
|
||||
async function createProduct() {
|
||||
@@ -31,8 +32,6 @@ async function createProduct() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1 class="subtitle is-4">AddProductForm</h1>
|
||||
<form class="fixed-grid has-12-cols">
|
||||
<div class="grid">
|
||||
<div class="cell is-col-span-8">
|
||||
@@ -57,6 +56,7 @@ async function createProduct() {
|
||||
field: 'name',
|
||||
column: ['name'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'OS',
|
||||
addon: {
|
||||
component: 'imports/addons/AddOS',
|
||||
@@ -78,6 +78,7 @@ async function createProduct() {
|
||||
field: 'name',
|
||||
column: ['name'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'Hãng',
|
||||
addon: {
|
||||
component: 'imports/addons/AddManufacturer',
|
||||
@@ -99,6 +100,7 @@ async function createProduct() {
|
||||
field: 'code',
|
||||
column: ['code'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'Pin',
|
||||
addon: {
|
||||
component: 'imports/addons/AddBattery',
|
||||
@@ -117,9 +119,10 @@ async function createProduct() {
|
||||
<SearchBox
|
||||
v-bind="{
|
||||
api: 'Screen',
|
||||
field: 'code',
|
||||
column: ['code'],
|
||||
field: 'label',
|
||||
column: ['resolution', 'standard', 'technology'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'Màn hình',
|
||||
addon: {
|
||||
component: 'imports/addons/AddScreen',
|
||||
@@ -141,6 +144,7 @@ async function createProduct() {
|
||||
field: 'name',
|
||||
column: ['name'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'CPU',
|
||||
addon: {
|
||||
component: 'imports/addons/AddCPU',
|
||||
@@ -162,6 +166,7 @@ async function createProduct() {
|
||||
field: 'name',
|
||||
column: ['name'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'GPU',
|
||||
addon: {
|
||||
component: 'imports/addons/AddGPU',
|
||||
@@ -183,6 +188,7 @@ async function createProduct() {
|
||||
field: 'code',
|
||||
column: ['code'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'Camera',
|
||||
addon: {
|
||||
component: 'imports/addons/AddCamera',
|
||||
@@ -204,6 +210,7 @@ async function createProduct() {
|
||||
field: 'code',
|
||||
column: ['code'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'SIM',
|
||||
addon: {
|
||||
component: 'imports/addons/AddSIM',
|
||||
@@ -225,6 +232,7 @@ async function createProduct() {
|
||||
field: 'name',
|
||||
column: ['name'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'Kết nối',
|
||||
addon: {
|
||||
component: 'imports/addons/AddNetworkTechnology',
|
||||
@@ -246,6 +254,7 @@ async function createProduct() {
|
||||
field: 'code',
|
||||
column: ['code'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'Công nghệ sạc',
|
||||
addon: {
|
||||
component: 'imports/addons/AddChargingTechnology',
|
||||
@@ -267,6 +276,7 @@ async function createProduct() {
|
||||
field: 'code',
|
||||
column: ['code'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'Bộ nhớ ngoài',
|
||||
addon: {
|
||||
component: 'imports/addons/AddExternalStorage',
|
||||
@@ -288,6 +298,7 @@ async function createProduct() {
|
||||
field: 'name',
|
||||
column: ['name'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'Chỉ số IP',
|
||||
addon: {
|
||||
component: 'imports/addons/AddIPRating',
|
||||
@@ -309,6 +320,7 @@ async function createProduct() {
|
||||
field: 'label',
|
||||
column: ['frame_material', 'back_material'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'Chất liệu',
|
||||
addon: {
|
||||
component: 'imports/addons/AddDesign',
|
||||
@@ -328,12 +340,11 @@ async function createProduct() {
|
||||
@click.prevent="createProduct"
|
||||
>
|
||||
<span class="icon">
|
||||
<Icon name="material-symbols:add-2-rounded" />
|
||||
<Icon name="material-symbols:add-rounded" />
|
||||
</span>
|
||||
<span>Tạo sản phẩm</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
61
app/components/imports/AddProductVariant.vue
Normal file
61
app/components/imports/AddProductVariant.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<script setup>
|
||||
import DataView from "@/components/datatable/DataView.vue";
|
||||
import AddProductVariantForm from "@/components/imports/AddProductVariantForm.vue";
|
||||
const product = ref();
|
||||
const key = ref(0);
|
||||
watch(product, () => {
|
||||
key.value++;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed-grid has-12-cols">
|
||||
<div class="grid">
|
||||
<div class="cell is-col-span-6 is-col-start-4">
|
||||
<div class="field">
|
||||
<label class="label">Sản phẩm</label>
|
||||
<SearchBox
|
||||
v-bind="{
|
||||
api: 'product',
|
||||
field: 'name',
|
||||
column: ['name'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'Tìm sản phẩm',
|
||||
addon: {
|
||||
component: 'imports/AddProductForm',
|
||||
width: '90%',
|
||||
height: 'auto',
|
||||
title: 'Thêm sản phẩm',
|
||||
},
|
||||
}"
|
||||
@option="product = $event"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="product"
|
||||
class="cell is-col-span-12"
|
||||
>
|
||||
<DataView
|
||||
:key="key"
|
||||
v-bind="{
|
||||
api: 'Product_Variant',
|
||||
setting: 'product-variants',
|
||||
pagename: 'product-variants',
|
||||
params: {
|
||||
values:
|
||||
'id,code,product,product__name,color,color__code,color__name,color__hex_code,ram,ram__code,ram__capacity,internal_storage,internal_storage__code,internal_storage__capacity,image,price,note,create_time,update_time',
|
||||
filter: { product: product.id },
|
||||
},
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<AddProductVariantForm
|
||||
v-if="product"
|
||||
:productId="product.id"
|
||||
@created="key++"
|
||||
/>
|
||||
</template>
|
||||
@@ -1,20 +1,33 @@
|
||||
<script setup>
|
||||
import InputNumber from "@/components/common/InputNumber.vue";
|
||||
import { omitBy } from "es-toolkit";
|
||||
|
||||
const { $insertapi } = useNuxtApp();
|
||||
const props = defineProps({
|
||||
productId: Number,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["created"]);
|
||||
|
||||
const { $empty, $insertapi } = useNuxtApp();
|
||||
const isPending = ref(false);
|
||||
const body = ref({
|
||||
product: null,
|
||||
product: props.productId,
|
||||
price: null,
|
||||
internal_storage: null,
|
||||
ram: null,
|
||||
color: null,
|
||||
note: null,
|
||||
note: "",
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.productId,
|
||||
(newVal) => {
|
||||
body.value.product = props.productId;
|
||||
},
|
||||
);
|
||||
|
||||
function selected(field, data) {
|
||||
if (field === "price") {
|
||||
if (data === null || field === "price") {
|
||||
body.value[field] = data;
|
||||
} else {
|
||||
body.value[field] = data.id;
|
||||
@@ -23,38 +36,21 @@ function selected(field, data) {
|
||||
|
||||
async function createProductVariant() {
|
||||
isPending.value = true;
|
||||
const res = await $insertapi("Product_Variant", body.value);
|
||||
const trimmedBody = omitBy(body.value, $empty);
|
||||
const res = await $insertapi("Product_Variant", trimmedBody);
|
||||
isPending.value = false;
|
||||
if (res !== "error") {
|
||||
emit("created");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1 class="subtitle is-4">AddProductVariantForm</h1>
|
||||
<h1 class="subtitle">Thêm phiên bản</h1>
|
||||
<form class="fixed-grid has-12-cols">
|
||||
<div class="grid">
|
||||
<div class="cell is-col-span-8">
|
||||
<div class="field">
|
||||
<label class="label">Sản phẩm</label>
|
||||
<SearchBox
|
||||
v-bind="{
|
||||
api: 'product',
|
||||
field: 'name',
|
||||
column: ['name'],
|
||||
first: true,
|
||||
placeholder: 'Sản phẩm',
|
||||
addon: {
|
||||
component: 'imports/AddProductForm',
|
||||
width: '90%',
|
||||
height: 'auto',
|
||||
title: 'Thêm sản phẩm',
|
||||
},
|
||||
}"
|
||||
@option="selected('product', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell is-col-span-4">
|
||||
<div class="cell is-col-span-3">
|
||||
<div class="field">
|
||||
<label class="label">Đơn giá</label>
|
||||
<div class="control">
|
||||
@@ -70,15 +66,16 @@ async function createProductVariant() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell is-col-span-4">
|
||||
<div class="cell is-col-span-3">
|
||||
<div class="field">
|
||||
<label class="label">Màu sắc</label>
|
||||
<SearchBox
|
||||
v-bind="{
|
||||
api: 'color',
|
||||
api: 'Color',
|
||||
field: 'name',
|
||||
column: ['name'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'Màu sắc',
|
||||
addon: {
|
||||
component: 'imports/addons/AddColor',
|
||||
@@ -91,7 +88,7 @@ async function createProductVariant() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell is-col-span-4">
|
||||
<div class="cell is-col-span-3">
|
||||
<div class="field">
|
||||
<label class="label">RAM</label>
|
||||
<SearchBox
|
||||
@@ -100,6 +97,7 @@ async function createProductVariant() {
|
||||
field: 'code',
|
||||
column: ['code'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'RAM',
|
||||
addon: {
|
||||
component: 'imports/addons/AddRAM',
|
||||
@@ -112,7 +110,7 @@ async function createProductVariant() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell is-col-span-4">
|
||||
<div class="cell is-col-span-3">
|
||||
<div class="field">
|
||||
<label class="label">Bộ nhớ trong</label>
|
||||
<SearchBox
|
||||
@@ -121,6 +119,7 @@ async function createProductVariant() {
|
||||
field: 'code',
|
||||
column: ['code'],
|
||||
first: true,
|
||||
clearable: true,
|
||||
placeholder: 'Bộ nhớ trong',
|
||||
addon: {
|
||||
component: 'imports/addons/AddInternalStorage',
|
||||
@@ -148,11 +147,14 @@ async function createProductVariant() {
|
||||
<div class="cell is-col-span-12">
|
||||
<button
|
||||
:class="['button is-primary', { 'is-loading': isPending }]"
|
||||
:disabled="Object.values(body).every((v) => v === null)"
|
||||
:disabled="Object.values(body).every($empty)"
|
||||
@click.prevent="createProductVariant"
|
||||
>
|
||||
<span class="icon">
|
||||
<Icon name="material-symbols:add-2-rounded" />
|
||||
<Icon
|
||||
name="material-symbols:add-rounded"
|
||||
:size="18"
|
||||
/>
|
||||
</span>
|
||||
<span>Thêm phiên bản</span>
|
||||
</button>
|
||||
|
||||
12
app/components/imports/Color.vue
Normal file
12
app/components/imports/Color.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
color: String,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="w-20 h-4"
|
||||
:style="{ backgroundColor: color, outline: '1px solid var(--bulma-grey-85)' }"
|
||||
></div>
|
||||
</template>
|
||||
@@ -1,22 +1,43 @@
|
||||
<script setup>
|
||||
import AddProductForm from "@/components/imports/AddProductForm.vue";
|
||||
import AddProductVariantForm from "@/components/imports/AddProductVariantForm.vue";
|
||||
import FileUpload from "@/components/media/FileUpload.vue";
|
||||
import AddProductVariant from "@/components/imports/AddProductVariant.vue";
|
||||
const menus = [
|
||||
{
|
||||
id: "add-product",
|
||||
name: "Tạo sản phẩm",
|
||||
},
|
||||
{
|
||||
id: "add-product-variant",
|
||||
name: "Thêm phiên bản",
|
||||
},
|
||||
];
|
||||
const activeMenu = ref(menus[1]);
|
||||
</script>
|
||||
<template>
|
||||
<FileUpload
|
||||
:type="['file']"
|
||||
class="mb-2"
|
||||
@files="onFiles"
|
||||
<div class="fixed-grid has-12-cols">
|
||||
<div class="grid is-gap-4">
|
||||
<div class="cell is-col-span-2">
|
||||
<aside class="menu">
|
||||
<ul class="menu-list">
|
||||
<li
|
||||
v-for="menu in menus"
|
||||
:key="menu.id"
|
||||
>
|
||||
<template #icon>
|
||||
<Icon
|
||||
name="material-symbols:upload-rounded"
|
||||
:size="20"
|
||||
/>
|
||||
</template>
|
||||
<span class="font-medium">Import</span>
|
||||
</FileUpload>
|
||||
<AddProductForm />
|
||||
<AddProductVariantForm />
|
||||
<a
|
||||
@click="activeMenu = menu"
|
||||
:class="{
|
||||
'is-active': activeMenu.id === menu.id,
|
||||
}"
|
||||
>{{ menu.name }}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
</div>
|
||||
<div class="cell is-col-span-10">
|
||||
<AddProductForm v-if="activeMenu.id === 'add-product'" />
|
||||
<AddProductVariant v-if="activeMenu.id === 'add-product-variant'" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -71,7 +71,7 @@ const inventoryHighlights = [
|
||||
<span class="icon">
|
||||
<Icon
|
||||
:size="18"
|
||||
name="material-symbols:add-2-rounded"
|
||||
name="material-symbols:add-rounded"
|
||||
/>
|
||||
</span>
|
||||
<span>Điều chỉnh</span>
|
||||
|
||||
@@ -75,7 +75,7 @@ const viewMode = ref("list");
|
||||
<span class="icon">
|
||||
<Icon
|
||||
:size="18"
|
||||
name="material-symbols:add-2-rounded"
|
||||
name="material-symbols:add-rounded"
|
||||
/>
|
||||
</span>
|
||||
<span>Tạo đơn hàng</span>
|
||||
|
||||
@@ -151,7 +151,7 @@ export default {
|
||||
component: "menu/MenuSave",
|
||||
title: "Lưu thiết lập",
|
||||
width: "600px",
|
||||
height: "300px",
|
||||
height: "auto",
|
||||
vbind: { pagename: this.pagename3, classify: 3 },
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<template>
|
||||
<div
|
||||
style="min-height: 100vh"
|
||||
class="has-background-blue-100"
|
||||
data-theme="light"
|
||||
lang="vi"
|
||||
>
|
||||
@@ -28,6 +26,7 @@ import { onMounted } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import SnackBar from "@/components/snackbar/SnackBar.vue";
|
||||
import Modal from "@/components/Modal.vue";
|
||||
import { throttle } from "es-toolkit";
|
||||
|
||||
const route = useRoute();
|
||||
const { $getdata, $requestLogin, $store } = useNuxtApp();
|
||||
@@ -36,16 +35,16 @@ const snackbar = ref(undefined);
|
||||
const showmodal = ref(undefined);
|
||||
function getViewport() {
|
||||
let viewport;
|
||||
var width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
|
||||
const width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
|
||||
if (width <= 768)
|
||||
viewport = 1; //'mobile'
|
||||
viewport = 1; // 'mobile'
|
||||
else if (width >= 769 && width <= 1023)
|
||||
viewport = 2; //'tablet'
|
||||
viewport = 2; // 'tablet'
|
||||
else if (width >= 1024 && width <= 1215)
|
||||
viewport = 3; //'desktop'
|
||||
viewport = 3; // 'desktop'
|
||||
else if (width >= 1216 && width <= 1407)
|
||||
viewport = 4; //'widescreen'
|
||||
else if (width >= 1408) viewport = 5; //'fullhd'
|
||||
viewport = 4; // 'widescreen'
|
||||
else if (width >= 1408) viewport = 5; // 'fullhd'
|
||||
$store.commit("viewport", viewport);
|
||||
}
|
||||
async function checkRedirect() {
|
||||
@@ -91,6 +90,8 @@ async function checkLogin() {
|
||||
onMounted(() => {
|
||||
checkRedirect();
|
||||
getViewport();
|
||||
const throttledGetViewport = throttle(getViewport, 400);
|
||||
window.addEventListener("resize", throttledGetViewport);
|
||||
});
|
||||
watch(
|
||||
() => $store.snackbar,
|
||||
|
||||
@@ -59,6 +59,10 @@
|
||||
<script setup>
|
||||
useHead({
|
||||
link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.svg" }],
|
||||
htmlAttrs: {
|
||||
class: "has-background-blue-100",
|
||||
style: "min-height: 100vh",
|
||||
},
|
||||
});
|
||||
|
||||
const { $createMeta, $store, $copy, $id } = useNuxtApp();
|
||||
|
||||
@@ -4,6 +4,9 @@ export default defineNuxtPlugin(() => {
|
||||
|
||||
//==========Find & filter=================
|
||||
const find = function (arr, obj, attr) {
|
||||
if (typeof arr === "object" && !Array.isArray(arr)) {
|
||||
arr = Object.values(arr);
|
||||
}
|
||||
const keys = Object.keys(obj);
|
||||
let found = arr.find((v) => {
|
||||
let valid = true;
|
||||
|
||||
@@ -1012,7 +1012,16 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
name: "Screen",
|
||||
url: "data/Screen/",
|
||||
url_detail: "data-detail/Screen/",
|
||||
params: {},
|
||||
params: {
|
||||
values: "id,code,resolution,standard,technology,size,create_time",
|
||||
distinct_values: {
|
||||
label: {
|
||||
type: "Concat",
|
||||
field: ["resolution", "standard", "technology"],
|
||||
},
|
||||
},
|
||||
summary: "annotate",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "CPU",
|
||||
@@ -1078,7 +1087,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "color",
|
||||
name: "Color",
|
||||
url: "data/Color/",
|
||||
url_detail: "data-detail/Color/",
|
||||
params: {},
|
||||
@@ -1170,7 +1179,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
try {
|
||||
let found = findapi(name);
|
||||
let curpath = found.path ? paths.find((x) => x.name === found.path).url : path;
|
||||
var rs;
|
||||
let rs;
|
||||
if (!Array.isArray(data)) {
|
||||
rs = await $fetch(`${curpath}${found.url}`, {
|
||||
method: "POST",
|
||||
@@ -1241,7 +1250,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
? $snackbar("Data has been successfully saved to the system.", "Success", "Success")
|
||||
: $snackbar("Dữ liệu đã được lưu vào hệ thống", "Thành công", "Success");
|
||||
}
|
||||
return rs.data;
|
||||
return rs;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return "error";
|
||||
@@ -1450,7 +1459,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
let index = copy.findIndex((v) => v.id === id);
|
||||
if (index >= 0) $remove(copy, index);
|
||||
} else {
|
||||
rs.data.forEach((element) => {
|
||||
rs.forEach((element) => {
|
||||
let index = copy.findIndex((v) => v.id === element.id);
|
||||
if (index >= 0) $remove(copy, index);
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@ import AddDesign from "@/components/imports/addons/AddDesign.vue";
|
||||
import AddColor from "@/components/imports/addons/AddColor.vue";
|
||||
import AddRAM from "@/components/imports/addons/AddRAM.vue";
|
||||
import AddInternalStorage from "@/components/imports/addons/AddInternalStorage.vue";
|
||||
import Color from "@/components/imports/Color.vue";
|
||||
import Returns from "@/components/imports/Returns.vue";
|
||||
import Exports from "@/components/exports/Exports.vue";
|
||||
import ExportsDamaged from "@/components/exports/ExportsDamaged.vue";
|
||||
@@ -177,6 +178,7 @@ const components = {
|
||||
AddColor,
|
||||
AddRAM,
|
||||
AddInternalStorage,
|
||||
Color,
|
||||
Returns,
|
||||
Exports,
|
||||
ExportsDamaged,
|
||||
|
||||
Reference in New Issue
Block a user