Changes Update User

This commit is contained in:
Thien Pham Van
2026-02-24 11:06:46 +07:00
parent 24dfaf52c4
commit 06f78f95d7
9 changed files with 675 additions and 536 deletions

View File

@@ -49,24 +49,24 @@
<Modal @close="showmodal = undefined" v-bind="showmodal" v-if="showmodal"></Modal>
</template>
<script setup>
import { useStore } from "@/stores/index";
import { useStore } from '@/stores/index';
var props = defineProps({
data: Array,
info: Object,
});
const { $getdata, $getapi, $createField, $clone, $getpage, $empty, $copyToClipboard, $find } = useNuxtApp();
const { $getdata, $getapi, $createField, $clone, $getpage, $empty, $copyToClipboard, $find, $path } = useNuxtApp();
const store = useStore();
var pagename = "pagedata99";
var pagename = 'pagedata99';
var pagedata = ref();
pagedata.value = $getpage();
store.commit(pagename, pagedata);
let list = ["LogEntry", "Permission", "ContentType", "Session", "Group"];
let list = ['LogEntry', 'Permission', 'ContentType', 'Session', 'Group'];
var current = ref({ fields: [] });
var tabs = [
{ code: "datatype", name: "Kiểu dữ liệu" },
{ code: "table", name: "Dữ liệu" },
{ code: 'datatype', name: 'Kiểu dữ liệu' },
{ code: 'table', name: 'Dữ liệu' },
];
var tab = ref("datatype");
var tab = ref('datatype');
var datatable = ref();
var query = ref();
var values, filter;
@@ -78,11 +78,11 @@ function changeMenu(v) {
values = undefined;
filter = undefined;
current.value = v;
if (tab.value === "table") loadData();
if (tab.value === 'table') loadData();
}
async function changeTab(v) {
tab.value = v.code;
if (v.code === "table") loadData();
if (v.code === 'table') loadData();
}
async function loadData() {
let vfilter = filter ? filter.trim() : undefined;
@@ -90,26 +90,26 @@ async function loadData() {
try {
vfilter = JSON.parse(vfilter);
} catch (error) {
alert("Cấu trúc filter có lỗi");
alert('Cấu trúc filter có lỗi');
vfilter = undefined;
}
}
let params = { values: values ? values.trim() : undefined, filter: filter };
let modelName = current.value.model;
let found = {
name: modelName.toLowerCase().replace("_", ""),
name: modelName.toLowerCase().replace('_', ''),
url: `data/${modelName}/`,
url_detail: `data-detail/${modelName}/`,
params: params,
};
query.value = $clone(found);
let rs = await $getapi([found]);
if (rs === "error") return alert("Đã xảy ra lỗi, hãy xem lại câu lệnh.");
if (rs === 'error') return alert('Đã xảy ra lỗi, hãy xem lại câu lệnh.');
datatable.value = rs[0].data.rows;
showData();
// api query
const baseUrl = "https://api.y99.vn/" + `${query.value.url}`;
const baseUrl = $path + `${query.value.url}`;
apiUrl.value = baseUrl;
let vparams = !$empty(values) ? { values: values } : null;
if (!$empty(filter)) {
@@ -125,15 +125,15 @@ async function loadData() {
function showData() {
let arr = [];
if (!$empty(values)) {
let arr1 = values.trim().split(",");
let arr1 = values.trim().split(',');
arr1.map((v) => {
let val = v.trim();
let field = $createField(val, val, "string", true);
let field = $createField(val, val, 'string', true);
arr.push(field);
});
} else {
current.value.fields.map((v) => {
let field = $createField(v.name, v.name, "string", true);
let field = $createField(v.name, v.name, 'string', true);
arr.push(field);
});
}
@@ -148,10 +148,10 @@ function copy() {
}
function openModel(x) {
showmodal.value = {
component: "datatable/ModelInfo",
component: 'datatable/ModelInfo',
title: x.model,
width: "70%",
height: "600px",
width: '70%',
height: '600px',
vbind: { data: data, info: $find(data, { model: x.model }) },
};
}

View File

@@ -1,28 +1,28 @@
<template>
<div class="mx-2">
<a class="ml-5" @click="resetPassword">
<div class="mx-2 has-text-centered">
<a @click="resetPassword">
<SvgIcon v-bind="{ name: 'padlock.png', type: 'gray', size: 20 }"></SvgIcon>
</a>
</div>
</template>
<script setup>
const { $store } = useNuxtApp();
const { $store, $isVietnamese } = useNuxtApp();
const emit = defineEmits(["clickevent"]);
const emit = defineEmits(['clickevent']);
var props = defineProps({
appid: Number,
row: Object,
});
function resetPassword() {
emit("clickevent", {
name: "dataevent",
emit('clickevent', {
name: 'dataevent',
data: {
modal: {
title: $store.lang === "en" ? "Reset password" : "Đặt lại mật khẩu",
height: "300px",
width: "800px",
component: "user/ResetPassword",
title: $isVietnamese() ? 'Đặt lại mật khẩu' : 'Reset password',
height: '300px',
width: '800px',
component: 'user/ResetPassword',
vbind: { row: props.row },
},
},

View File

@@ -1,91 +1,125 @@
<template>
<div class="px-3">
<div class="field is-horizontal">
<div class="field-body">
<div class="field">
<label class="label">Username<b class="ml-1 has-text-danger">*</b></label>
<div class="control">
<input class="input" type="text" placeholder="" v-model="username">
</div>
<p class="help is-danger" v-if="errors.find(v=>v.name==='username')">{{errors.find(v=>v.name==='username').text}}</p>
<p class="help is-primary" v-else-if="info">{{info}}</p>
</div>
<div class="field" v-if="!dealer">
<label class="label">Account type<b class="ml-1 has-text-danger">*</b></label>
<div class="control">
<SearchBox v-bind="{api:'usertype', field:'name', column:['name'], first:true, position: 'top'}"
@option="selected('_type', $event)"></SearchBox>
</div>
<p class="help is-danger" v-if="errors.find(v=>v.name==='type')">{{errors.find(v=>v.name==='type').text}}</p>
</div>
</div>
</div>
<div class="field is-horizontal mt-4">
<div class="field-body">
<div class="field">
<label class="label">Full name<b class="ml-1 has-text-danger">*</b></label>
<div class="control">
<input class="input" type="text" placeholder="" v-model="fullname">
</div>
<p class="help is-danger" v-if="errors.find(v=>v.name==='fullname')">{{errors.find(v=>v.name==='fullname').text}}</p>
</div>
<div class="field">
<label class="label">Email<b class="ml-1 has-text-danger">*</b></label>
<div class="control">
<input class="input" type="text" placeholder="" v-model="email">
</div>
<p class="help is-danger" v-if="errors.find(v=>v.name==='email')">{{errors.find(v=>v.name==='email').text}}</p>
</div>
</div>
</div>
<div class="field is-horizontal mt-4">
<div class="field-body">
<div class="field">
<label class="label">Password<b class="ml-1 has-text-danger">*</b></label>
<div class="field has-addons">
<p class="control is-expanded">
<input class="input" :type="showpass? 'text' : 'password'" placeholder="At least 6 characters including letters and numbers." v-model="password">
</p>
<div class="control">
<a class="button" @click="showpass=!showpass">
<SvgIcon v-bind="{name: 'eye-off.svg', type: 'dark', size: 22}" v-if="showpass"></SvgIcon>
<SvgIcon v-bind="{name: 'view.svg', type: 'dark', size: 22}" v-else></SvgIcon>
</a>
</div>
</div>
<p class="help is-danger" v-if="errors.find(v=>v.name==='password')">{{errors.find(v=>v.name==='password').text}}</p>
</div>
<div class="field">
<label class="label">Retype password<b class="ml-1 has-text-danger">*</b></label>
<p class="control is-expanded">
<input class="input" :type="showpass? 'text' : 'password'" placeholder="" v-model="retypePassword">
</p>
<div class="px-3">
<div class="field is-horizontal">
<div class="field-body">
<div class="field">
<label class="label"
>{{ isVietnamese ? 'Tên tài khoản' : 'Username' }}<b class="ml-1 has-text-danger">*</b></label
>
<div class="control">
<input class="input" type="text" placeholder="" v-model="username" />
</div>
<p class="help is-danger" v-if="errors.find((v) => v.name === 'username')">
{{ errors.find((v) => v.name === 'username').text }}
</p>
<p class="help is-primary" v-else-if="info">{{ info }}</p>
</div>
<div class="field" v-if="!dealer">
<label class="label"
>{{ isVietnamese ? 'Loại tài khoản' : 'Account type' }}<b class="ml-1 has-text-danger">*</b></label
>
<div class="control">
<SearchBox
v-bind="{ api: 'usertype', field: 'name', column: ['name'], first: true, position: 'top' }"
@option="selected('_type', $event)"
></SearchBox>
</div>
<p class="help is-danger" v-if="errors.find((v) => v.name === 'type')">
{{ errors.find((v) => v.name === 'type').text }}
</p>
</div>
</div>
</div>
<p class="help is-danger" v-if="errors.find(v=>v.name==='retypePassword')">{{errors.find(v=>v.name==='retypePassword').text}}</p>
</div>
</div>
<div class="mt-5 pt-2">
<button :class="`button is-primary has-text-white ${ loading? 'is-loading' : ''}`" @click="createAccount()" v-if="enable">Tạo tài khoản</button>
</div>
</div>
<div class="field is-horizontal mt-4">
<div class="field-body">
<div class="field">
<label class="label"
>{{ isVietnamese ? 'Họ và tên' : 'Full name' }}<b class="ml-1 has-text-danger">*</b></label
>
<div class="control">
<input class="input" type="text" placeholder="" v-model="fullname" />
</div>
<p class="help is-danger" v-if="errors.find((v) => v.name === 'fullname')">
{{ errors.find((v) => v.name === 'fullname').text }}
</p>
</div>
<div class="field">
<label class="label">{{ isVietnamese ? 'Email' : 'Email' }}<b class="ml-1 has-text-danger">*</b></label>
<div class="control">
<input class="input" type="text" placeholder="" v-model="email" />
</div>
<p class="help is-danger" v-if="errors.find((v) => v.name === 'email')">
{{ errors.find((v) => v.name === 'email').text }}
</p>
</div>
</div>
</div>
<div class="field is-horizontal mt-4">
<div class="field-body">
<div class="field">
<label class="label">{{ isVietnamese ? 'Mật khẩu' : 'Password' }}<b class="ml-1 has-text-danger">*</b></label>
<div class="field has-addons">
<p class="control is-expanded">
<input
class="input"
:type="showpass ? 'text' : 'password'"
placeholder="At least 6 characters including letters and numbers."
v-model="password"
/>
</p>
<div class="control">
<a class="button" @click="showpass = !showpass">
<SvgIcon v-bind="{ name: 'eye-off.svg', type: 'dark', size: 22 }" v-if="showpass"></SvgIcon>
<SvgIcon v-bind="{ name: 'view.svg', type: 'dark', size: 22 }" v-else></SvgIcon>
</a>
</div>
</div>
<p class="help is-danger" v-if="errors.find((v) => v.name === 'password')">
{{ errors.find((v) => v.name === 'password').text }}
</p>
</div>
<div class="field">
<label class="label"
>{{ isVietnamese ? 'Nhập lại mật khẩu' : 'Retype password' }}<b class="ml-1 has-text-danger">*</b></label
>
<p class="control is-expanded">
<input class="input" :type="showpass ? 'text' : 'password'" placeholder="" v-model="retypePassword" />
</p>
</div>
<p class="help is-danger" v-if="errors.find((v) => v.name === 'retypePassword')">
{{ errors.find((v) => v.name === 'retypePassword').text }}
</p>
</div>
</div>
<div class="mt-5 pt-2">
<button
:class="`button is-primary has-text-white ${loading ? 'is-loading' : ''}`"
@click="createAccount()"
v-if="enable"
>
{{ isVietnamese ? 'Tạo tài khoản' : 'Create account' }}
</button>
</div>
</div>
</template>
<script>
import { useStore } from '~/stores/index'
import { useStore } from '~/stores/index';
export default {
setup() {
const store = useStore()
return {store}
const store = useStore();
return { store };
},
props: ['pagename', 'row', 'api', 'dealer'],
data () {
data() {
return {
isVietnamese: this.$isVietnamese(),
fullname: undefined,
username: undefined,
email: undefined,
password: undefined,
retypePassword: undefined,
errors: [],
info: 'User name must not contain any spaces.',
info: undefined,
showpass: true,
hash: undefined,
status: undefined,
@@ -97,140 +131,175 @@ export default {
check: {},
branchOpt: undefined,
enable: true,
loading: false
}
loading: false,
};
},
created() {
if(!this.row) return
this.fullname = this.row.fullname
this.email = this.row.email
if(this.row.code) this.username = this.row.code.toLocaleLowerCase()
if (!this.row) return;
this.fullname = this.row.fullname;
this.email = this.row.email;
if (this.row.code) this.username = this.row.code.toLocaleLowerCase();
},
mounted() {
let pass = this.$id().toLocaleLowerCase()
this.password = pass
this.retypePassword = pass
window.addEventListener("keyup", (ev) => ev.key==='Enter' && this.$route.name==='signup'? this.createAccount() : false)
let pass = this.$id().toLocaleLowerCase();
this.password = pass;
this.retypePassword = pass;
window.addEventListener('keyup', (ev) =>
ev.key === 'Enter' && this.$route.name === 'signup' ? this.createAccount() : false,
);
},
computed: {
registermethod: {
get: function() {return this.store.registermethod},
set: function(val) {this.$store.commit("updateRegisterMethod", {registermethod: val})}
get: function () {
return this.store.registermethod;
},
set: function (val) {
this.$store.commit('updateRegisterMethod', { registermethod: val });
},
},
authmethod: {
get: function() {return this.store.authmethod},
set: function(val) {this.$store.commit("updateAuthMethod", {authmethod: val})}
get: function () {
return this.store.authmethod;
},
set: function (val) {
this.$store.commit('updateAuthMethod', { authmethod: val });
},
},
authstatus: {
get: function() {return this.store.authstatus},
set: function(val) {this.$store.commit("updateAuthStatus", {authstatus: val})}
get: function () {
return this.store.authstatus;
},
set: function (val) {
this.$store.commit('updateAuthStatus', { authstatus: val });
},
},
usertype: {
get: function() {return this.store.usertype},
set: function(val) {this.$store.commit("updateUserType", {usertype: val})}
get: function () {
return this.store.usertype;
},
set: function (val) {
this.$store.commit('updateUserType', { usertype: val });
},
},
dialog: {
get: function() {return this.store['dialog']},
set: function(val) {this.$store.commit('updateStore', {name: 'dialog', data: val})}
}
get: function () {
return this.store['dialog'];
},
set: function (val) {
this.$store.commit('updateStore', { name: 'dialog', data: val });
},
},
},
methods: {
checkError () {
this.errors = []
if (!this.$empty(this.fullname)) { this.fullname = this.fullname.trim() }
if (!this.$empty(this.username)) { this.username = this.username.trim().toLowerCase() }
checkError() {
this.errors = [];
if (!this.$empty(this.fullname)) {
this.fullname = this.fullname.trim();
}
if (!this.$empty(this.username)) {
this.username = this.username.trim().toLowerCase();
}
if (this.$empty(this.fullname)) {
this.errors.push({ name: 'fullname', text: 'Họ và tên không được bỏ trống' })
this.errors.push({ name: 'fullname', text: 'Họ và tên không được bỏ trống' });
} else if (this.fullname.length < 5) {
this.errors.push({ name: 'fullname', text: 'Họ và tên quá ngắn. Yêu cầu từ 5 kí tự trở nên' })
this.errors.push({ name: 'fullname', text: 'Họ và tên quá ngắn. Yêu cầu từ 5 kí tự trở nên' });
}
if (this.$empty(this.username)) {
this.errors.push({ name: 'username', text: 'Tài khoản không được bỏ trống' })
} else if (this.username!==this.username.replace(' ', '')) {
this.errors.push({ name: 'username', text: 'Tài khoản không được chứa khoảng trắng' })
} else if(this.username.length<5) {
this.errors.push({ name: 'fullname', text: 'Tài khoản quá ngắn. Yêu cầu từ 5 tự trở nên' })
this.errors.push({ name: 'username', text: 'Tài khoản không được bỏ trống' });
} else if (this.username !== this.username.replace(' ', '')) {
this.errors.push({ name: 'username', text: 'Tài khoản không được chứa khoảng trắng' });
} else if (this.username.length < 5) {
this.errors.push({ name: 'fullname', text: 'Tài khoản quá ngắn. Yêu cầu từ 5 kí tự trở nên' });
}
if (this.$empty(this.password)) {
this.errors.push({ name: 'password', text: 'Mật khẩu không được bỏ trống' })
this.errors.push({ name: 'password', text: 'Mật khẩu không được bỏ trống' });
} else if (this.password.length < 6) {
this.errors.push({ name: 'password', text: 'Mật khẩu gồm 6 tự trở nên bao gồm chữ số ' })
this.errors.push({ name: 'password', text: 'Mật khẩu gồm 6 kí tự trở nên bao gồm chữ và số ' });
} else if (!(/\d/.test(this.password) && /[a-zA-Z]/.test(this.password))) {
this.errors.push({ name: 'password', text: 'Mật khẩu gồm 6 tự trở nên bao gồm chữ số ' })
this.errors.push({ name: 'password', text: 'Mật khẩu gồm 6 kí tự trở nên bao gồm chữ và số ' });
}
if (this.$empty(this.retypePassword)) {
this.errors.push({ name: 'retypePassword', text: 'Nhắc lại mật khẩu không được bỏ trống' })
this.errors.push({ name: 'retypePassword', text: 'Nhắc lại mật khẩu không được bỏ trống' });
} else if (this.password !== this.retypePassword) {
this.errors.push({ name: 'retypePassword', text: 'Nhắc lại mật khẩu phải giống với mật khẩu đã nhập' })
this.errors.push({ name: 'retypePassword', text: 'Nhắc lại mật khẩu phải giống với mật khẩu đã nhập' });
}
if(!this.$empty(this.email)) {
this.email = this.email.trim()
if(this.$errEmail(this.email)) this.errors.push({ name: 'email', text: 'Email không hợp lệ.' })
if (!this.$empty(this.email)) {
this.email = this.email.trim();
if (this.$errEmail(this.email)) this.errors.push({ name: 'email', text: 'Email không hợp lệ.' });
} else {
this.errors.push({ name: 'email', text: 'Email không được bỏ trống.' })
this.errors.push({ name: 'email', text: 'Email không được bỏ trống.' });
}
if(this.$empty(this.option)) {
if(this.dealer) {
this.option = {id: 3}
} else this.errors.push({ name: 'type', text: 'Chưa chọn loại tài khoản.' })
if (this.$empty(this.option)) {
if (this.dealer) {
this.option = { id: 3 };
} else this.errors.push({ name: 'type', text: 'Chưa chọn loại tài khoản.' });
}
let opts = this.radio==='all'? 'all' : []
if(opts.length===0) {
let opts = this.radio === 'all' ? 'all' : [];
if (opts.length === 0) {
for (const [key, value] of Object.entries(this.check)) {
if(value) opts.push(key)
if (value) opts.push(key);
}
}
return this.errors.length>0? true : false
return this.errors.length > 0 ? true : false;
},
async createAccount() {
this.loading = true
if(this.checkError()) return this.loading = false
const response = await fetch(`${this.$getpath()}password/${this.password}/`)
if(!response.ok) {
this.loading = true;
if (this.checkError()) return (this.loading = false);
const response = await fetch(`${this.$getpath()}password/${this.password}/`);
if (!response.ok) {
throw new Error(`Response status: ${response.status}`);
}
this.hash = await response.json();
let data = await this.$getdata('user', {username: this.username}, undefined, true)
if(data) {
this.loading = false
return this.errors.push({name: 'username', text: 'Tài khoản đã tồn tại trong hệ thống'})
let data = await this.$getdata('user', { username: this.username }, undefined, true);
if (data) {
this.loading = false;
return this.errors.push({ name: 'username', text: 'Tài khoản đã tồn tại trong hệ thống' });
}
let found = this.$findapi('user')
data = { fullname: this.fullname, username: this.username, phone: this.$empty(this.phone)? undefined : this.phone, password:
this.hash, type: this.option.id, register_method: 1, auth_status: 2, auth_method: 1, email: this.email}
this.user = await this.$insertrow('user', data, found.params.values, this.pagename)
if(this.user==='error') return this.loading = false
if(this.row) {
let copy = this.$copy(this.row)
copy.user = this.user.id
await this.$updaterow(this.api, copy, found.params.values, this.pagename)
let found = this.$findapi('user');
data = {
fullname: this.fullname,
username: this.username,
phone: this.$empty(this.phone) ? undefined : this.phone,
password: this.hash,
type: this.option.id,
register_method: 1,
auth_status: 2,
auth_method: 1,
email: this.email,
};
this.user = await this.$insertrow('user', data, found.params.values, this.pagename);
if (this.user === 'error') return (this.loading = false);
if (this.row) {
let copy = this.$copy(this.row);
copy.user = this.user.id;
await this.$updaterow(this.api, copy, found.params.values, this.pagename);
} else {
//send email
if(this.email) {
let content = `<p>Xin chào ${this.fullname}, </p>`
content += '<p>Tài khoản đăng nhập của bạn đã được khởi tạo và sẵn sàng sử dụng:</p>'
content += `<p>Username: ${this.username}</p>`
content += `<p>Password: ${this.password}</p>`
content += `<p>Đội ngũ Utopia.</p>`
let info = {subject: 'Tài khoản đăng nhập Utopia', to: this.email, sender: 1, content: content}
let rs = await this.$insertapi('sendemail', info)
if (this.email) {
let content = `<p>Xin chào ${this.fullname}, </p>`;
content += '<p>Tài khoản đăng nhập của bạn đã được khởi tạo và sẵn sàng sử dụng:</p>';
content += `<p>Username: ${this.username}</p>`;
content += `<p>Password: ${this.password}</p>`;
content += `<p>Đội ngũ Utopia.</p>`;
let info = { subject: 'Tài khoản đăng nhập Utopia', to: this.email, sender: 1, content: content };
let rs = await this.$insertapi('sendemail', info);
}
}
if(this.dealer) {
let copy = this.$copy(this.row)
copy.user = this.user.id
await this.$updaterow('dealer', copy, null, this.pagename)
if (this.dealer) {
let copy = this.$copy(this.row);
copy.user = this.user.id;
await this.$updaterow('dealer', copy, null, this.pagename);
}
this.loading = false
this.$dialog('Tạo tài khoản thành công.', 'Thành công', 'Success', 10)
this.$emit('close')
this.loading = false;
this.$dialog('Tạo tài khoản thành công.', 'Thành công', 'Success', 10);
this.$emit('close');
},
selected(attr, obj) {
this.option = obj
this.option = obj;
},
doCheck(v) {
this.$set(this.check, v.code, this.check[v.code]? false : true)
}
}
}
this.$set(this.check, v.code, this.check[v.code] ? false : true);
},
},
};
</script>

View File

@@ -1,52 +1,67 @@
<template>
<div v-if="record">
<div class="columns is-multiline mx-0">
<div class="column is-4">
<div class="field">
<label class="label">Username<b class="ml-1 has-text-danger">*</b></label>
<div class="control">
<input class="input" type="text" placeholder="" v-model="record.username" disabled>
</div>
</div>
</div>
<div class="column is-4">
<div class="field">
<label class="label">Full name<b class="ml-1 has-text-danger">*</b></label>
<div class="control">
<input class="input" type="text" placeholder="" v-model="record.fullname">
</div>
<p class="help is-danger" v-if="errors.fullname">{{errors.fullname}}</p>
</div>
</div>
<div class="column is-4">
<div class="field">
<label class="label">Account type<b class="ml-1 has-text-danger">*</b></label>
<div class="control">
<SearchBox v-bind="{api:'usertype', field:'name', column:['name'], first:true, optionid:record.type, disabled: true}"
@option="selected('_type', $event)"></SearchBox>
</div>
<p class="help is-danger" v-if="errors.type">{{errors.type}}</p>
</div>
</div>
<div class="column is-4">
<div class="field">
<label class="label">Status<b class="ml-1 has-text-danger">*</b></label>
<div class="control">
<p class="mt-4">
<span class="mr-4" v-for="(v,i) in option">
<a class="icon-text" @click="radioBlocked=v">
<SvgIcon v-bind="{name: `radio-${radioBlocked.code===v.code? '' : 'un'}checked.svg`, type: 'gray', size: 22}"></SvgIcon>
<span>{{ v.name }}</span>
</a>
</span>
</p>
<div class="column is-4">
<div class="field">
<label class="label">{{isVietnamese ? 'Tên tài khoản' : 'Username'}}<b class="ml-1 has-text-danger">*</b></label>
<div class="control">
<input class="input" type="text" placeholder="" v-model="record.username" disabled />
</div>
</div>
</div>
<div class="column is-4">
<div class="field">
<label class="label">{{isVietnamese ? 'Họ tên' : 'Full name'}}<b class="ml-1 has-text-danger">*</b></label>
<div class="control">
<input class="input" type="text" placeholder="" v-model="record.fullname" />
</div>
<p class="help is-danger" v-if="errors.fullname">{{ errors.fullname }}</p>
</div>
</div>
<div class="column is-4">
<div class="field">
<label class="label">{{isVietnamese ? 'Loại tài khoản' : 'Account type'}}<b class="ml-1 has-text-danger">*</b></label>
<div class="control">
<SearchBox
v-bind="{
api: 'usertype',
field: 'name',
column: ['name'],
first: true,
optionid: record.type,
disabled: false,
}"
@option="selected('_type', $event)"
></SearchBox>
</div>
<p class="help is-danger" v-if="errors.type">{{ errors.type }}</p>
</div>
</div>
<div class="column is-4">
<div class="field">
<label class="label">{{ isVietnamese ? 'Trạng thái' : 'Status' }}<b class="ml-1 has-text-danger">*</b></label>
<div class="control">
<p class="mt-4">
<span class="mr-4" v-for="(v, i) in option">
<a class="icon-text" @click="radioBlocked = v">
<SvgIcon
v-bind="{
name: `radio-${radioBlocked?.code === v.code ? '' : 'un'}checked.svg`,
type: 'gray',
size: 22,
}"
></SvgIcon>
<span>{{ v.name }}</span>
</a>
</span>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="mt-5">
<button class="button is-primary has-text-white" @click="update()">Update</button>
</div>
<button class="button is-primary has-text-white" @click="update()">{{isVietnamese ? 'Cập nhật' : 'Update'}}</button>
</div>
</div>
</template>
<script>
@@ -54,58 +69,80 @@ export default {
props: ['pagename', 'row'],
data() {
return {
isVietnamese: this.$isVietnamese(),
errors: {},
record: this.row? this.$copy(this.row) : undefined,
record: this.row ? this.$copy(this.row) : undefined,
radioBlocked: undefined,
branch: [],
check: {},
branchOpt: undefined,
option: [{code: 0, name: 'Active'}, {code: 1, name: 'Blocked'}]
}
option: [
{ code: 0, name: 'Active' },
{ code: 1, name: 'Blocked' },
],
};
},
computed: {
option() {
return [
{ code: 0, name: this.isVietnamese ? 'Hoạt động' : 'Active' },
{ code: 1, name: this.isVietnamese ? 'Khóa' : 'Blocked' },
];
},
},
async created() {
this.record = await this.$getdata('user', undefined, {filter: {id: this.row.id}}, true)
this.radioBlocked = this.record.blocked? this.option[1] : this.option[0]
this.record = await this.$getdata('user', undefined, { filter: { id: this.row.id } }, true);
this.radioBlocked = this.record.blocked ? this.option[1] : this.option[0];
console.log(this.radioBlocked);
},
methods: {
selected(attr, obj) {
this.record[attr] = obj
this.record[attr] = obj;
},
checkError () {
this.errors = []
if (!this.$empty(this.record.fullname)) { this.record.fullname = this.record.fullname.trim() }
if (!this.$empty(this.record.username)) { this.record.username = this.record.username.trim().toLowerCase() }
checkError() {
this.errors = [];
if (!this.$empty(this.record.fullname)) {
this.record.fullname = this.record.fullname.trim();
}
if (!this.$empty(this.record.username)) {
this.record.username = this.record.username.trim().toLowerCase();
}
if (this.$empty(this.record.fullname)) {
this.errors.fullname = 'Họ và tên không được bỏ trống'
this.errors.fullname = 'Họ và tên không được bỏ trống';
} else if (this.record.fullname.length < 5) {
this.errors.fullname = 'Họ và tên quá ngắn. Yêu cầu từ 5 kí tự trở nên'
this.errors.fullname = 'Họ và tên quá ngắn. Yêu cầu từ 5 kí tự trở nên';
}
if (this.$empty(this.record.username)) {
this.errors.username = 'Tài khoản không được bỏ trống'
} else if (this.record.username!==this.record.username.replace(' ', '')) {
this.errors.username = 'Tài khoản không được chứa khoảng trắng'
} else if(this.record.username.length<5) {
this.errors.fullname = 'Tài khoản quá ngắn. Yêu cầu từ 5 tự trở nên'
this.errors.username = 'Tài khoản không được bỏ trống';
} else if (this.record.username !== this.record.username.replace(' ', '')) {
this.errors.username = 'Tài khoản không được chứa khoảng trắng';
} else if (this.record.username.length < 5) {
this.errors.fullname = 'Tài khoản quá ngắn. Yêu cầu từ 5 kí tự trở nên';
}
if(!(this.record._type || this.record.type)) this.errors.type = 'Chưa chọn loại tài khoản'
return this.errors.length>0? true : false
if (!(this.record._type || this.record.type)) this.errors.type = 'Chưa chọn loại tài khoản';
return this.errors.length > 0 ? true : false;
},
async update() {
if(this.checkError()) return
if(this.record._type) this.record.type = this.record._type.id
this.record.blocked = this.radioBlocked.code
let rs = await this.$updaterow('user', this.record, undefined, this.pagename)
if(this.radioBlocked.code===1) this.setTokenExpiry()
if (this.checkError()) return;
if (this.record._type) this.record.type = this.record._type.id;
this.record.blocked = this.radioBlocked.code;
let rs = await this.$updaterow('user', this.record, undefined, this.pagename);
if (this.radioBlocked.code === 1) this.setTokenExpiry();
},
doCheck(v) {
this.$set(this.check, v.code, this.check[v.code]? false : true)
this.$set(this.check, v.code, this.check[v.code] ? false : true);
},
async setTokenExpiry() {
let rows = await this.$getdata('token', {user: this.record.id, expiry: 0})
if(rows.length===0) return
rows.map(v=>v.expiry = 1)
this.$insertapi('token', rows, undefined, true)
}
}
}
</script>
let rows = await this.$getdata('token', { user: this.record.id, expiry: 0 });
if (rows.length === 0) return;
rows.map((v) => (v.expiry = 1));
this.$insertapi('token', rows, undefined, true);
},
},
mounted() {
console.log('User:', this.radioBlocked);
},
};
</script>

View File

@@ -3,13 +3,13 @@
<div class="field is-horizontal">
<div class="field-body columns">
<div class="field column">
<label class="label">Username</label>
<label class="label">{{ isVietnamese ? 'Tên tài khoản' : 'Username' }}</label>
<div class="control">
<p>{{ props.row.username }}</p>
</div>
</div>
<div class="field column">
<label class="label">{{ isVietnamese ? "Loại tài khoản" : "Account type" }}</label>
<label class="label">{{ isVietnamese ? 'Loại tài khoản' : 'Account type' }}</label>
<div class="control">
<p>{{ isVietnamese ? props.row.type__name : props.row.type__en }}</p>
</div>
@@ -19,7 +19,7 @@
<div class="field is-horizontal mt-4">
<div class="field-body columns">
<div class="field column">
<label class="label">{{ isVietnamese ? "Họ tên" : "Full name" }}</label>
<label class="label">{{ isVietnamese ? 'Họ tên' : 'Full name' }}</label>
<div class="control">
<p>{{ props.row.fullname }}</p>
</div>
@@ -34,15 +34,15 @@
</div>
<div class="mt-5 pt-2">
<button :class="`button is-primary has-text-white ${showModal.loading ? 'is-loading' : ''}`" @click="showConfirm">
{{ isVietnamese ? "Đặt lại mật khẩu" : "Reset password" }}
{{ isVietnamese ? 'Đặt lại mật khẩu' : 'Reset password' }}
</button>
</div>
<Modal v-bind="showModal" v-if="showModal.show" @close="showModal.show = false"> </Modal>
</div>
</template>
<script setup>
const { $store, $id, $insertapi, $getdata, $getpath, $updateapi, $dialog, $snackbar } = useNuxtApp();
const emit = defineEmits(["close"]);
const { $store, $id, $insertapi, $getdata, $getpath, $updateapi, $dialog, $snackbar, $isVietnamese } = useNuxtApp();
const emit = defineEmits(['close']);
var props = defineProps({
row: Object,
});
@@ -52,47 +52,51 @@ const showModal = ref({
loading: false,
});
const isVietnamese = computed(() => $store.lang.toLowerCase() === "vi");
const isVietnamese = computed(() => $isVietnamese());
let pass = $id().toLocaleLowerCase();
async function handlerResetPassword() {
showModal.value.loading = true;
let user = await $getdata("user", undefined, { filter: { id: props.row.id } }, true);
let user = await $getdata('user', undefined, { filter: { id: props.row.id } }, true);
const response = await fetch(`${$getpath()}password/${pass}/`);
let hash = await response.json();
user.password = hash;
let rs1 = await $updateapi("user", user, null, false);
if (rs1 === "error") {
$dialog("Có lỗi xảy ra. Hãy thử lại một lần nữa", "Lỗi", "Error");
let rs1 = await $updateapi('user', user, null, false);
if (rs1 === 'error') {
$dialog('Có lỗi xảy ra. Hãy thử lại một lần nữa', 'Lỗi', 'Error');
} else {
showModal.value.show = false;
let content = `<p>Xin chào ${props.row.fullname}, </p>`;
content += "<p>Mật khẩu tài khoản của bạn trên hệ thống đã được đặt lại thành công.</p>";
content += "<p>Tài khoản đăng nhập của bạn:</p>";
content += '<p>Mật khẩu tài khoản của bạn trên hệ thống đã được đặt lại thành công.</p>';
content += '<p>Tài khoản đăng nhập của bạn:</p>';
content += `<p>Username: ${props.row.username}</p>`;
content += `<p>Password: ${pass}</p>`;
content += `<p>Đội ngũ Utopia.</p>`;
let info = {
subject: "Thông báo đặt lại mật khẩu tài khoản đăng nhập Utopia",
subject: 'Thông báo đặt lại mật khẩu tài khoản đăng nhập Utopia',
to: props.row.email,
sender: 1,
content: content,
};
let rs = await $insertapi("sendemail", info, null, false);
let rs = await $insertapi('sendemail', info, null, false);
showModal.value.loading = false;
$snackbar("Mật khẩu đã được đặt lại thành công. Thông tin đã được gửi tới địa chỉ email đã đăng ký.", "Thành công", "Success");
emit("close");
$snackbar(
'Mật khẩu đã được đặt lại thành công. Thông tin đã được gửi tới địa chỉ email đã đăng ký.',
'Thành công',
'Success',
);
emit('close');
}
}
function showConfirm() {
showModal.value = {
show: true,
title: !isVietnamese ? "Confirm password reset" : "Xác nhận đặt lại mật khẩu",
height: "300px",
width: "800px",
component: "dialog/Confirm",
title: !isVietnamese ? 'Confirm password reset' : 'Xác nhận đặt lại mật khẩu',
height: '300px',
width: '800px',
component: 'dialog/Confirm',
vbind: {
content: `Bạn có muốn đặt lại mật khẩu cho tài khoản ${props.row.username?.toUpperCase()} này không?`,
duration: 10,

View File

@@ -21,10 +21,10 @@
</template>
<script setup>
const {$createMeta} = useNuxtApp()
const text = 'Rất tiếc! Có vẻ như đã xảy ra sự cố. Vui lòng thử lại hoặc liên hệ support@y99.vn nếu vấn đề vẫn tiếp diễn. Xin cảm ơn!'
const text = 'Rất tiếc! Có vẻ như đã xảy ra sự cố. Vui lòng thử lại hoặc liên hệ bộ phận hỗ trợ nếu vấn đề vẫn tiếp diễn. Xin cảm ơn!'
useHead(
$createMeta({title: 'Thông báo mới nhất | Y99', description: text,
$createMeta({title: 'Thông báo mới nhất | Utopia', description: text,
image: '/logo.png', type: 'website',
keywords: 'Y99'})
keywords: 'Utopia'})
)
</script>

View File

@@ -39,9 +39,9 @@ const changeTab = function(_tab, _subtab) {
let meta = {
title: currentTab[store.lang],
image: undefined,
description: 'Y99 - Account',
description: 'Utopia - Account',
type: 'article',
keywords: 'y99, account'
keywords: 'Utopia, account'
}
useHead($createMeta(meta))
}

View File

@@ -1,260 +1,282 @@
// nuxt 3 - plugins/my-plugin.ts
import { useRoute } from 'vue-router'
import { useStore } from '~/stores/index'
import dayjs from 'dayjs'
import weekday from 'dayjs/plugin/weekday'
import weekOfYear from 'dayjs/plugin/weekOfYear'
import relativeTime from 'dayjs/plugin/relativeTime'
dayjs.extend(weekday)
dayjs.extend(weekOfYear)
dayjs.extend(relativeTime)
import { useRoute } from 'vue-router';
import { useStore } from '~/stores/index';
import dayjs from 'dayjs';
import weekday from 'dayjs/plugin/weekday';
import weekOfYear from 'dayjs/plugin/weekOfYear';
import relativeTime from 'dayjs/plugin/relativeTime';
dayjs.extend(weekday);
dayjs.extend(weekOfYear);
dayjs.extend(relativeTime);
export default defineNuxtPlugin(() => {
const route = useRoute()
const {$id, $empty} = useNuxtApp()
const store = useStore()
const dialog = function(content, title, type, duration, width, height, vbind) {
content = typeof content === 'string'? content : JSON.stringify(content)
let vtitle = type==='Success'? `<span class="has-text-primary">${title}</span>` : title
if(type==='Error') vtitle = `<span class="has-text-danger">${title}</span>`
let data = {id: $id() , component: `dialog/${type || 'Info'}`,
vbind: {content: content, duration: duration, vbind: vbind}, title: vtitle, width: width || '600px', height: height || '100px'}
store.commit('showmodal', data)
}
const route = useRoute();
const { $id, $empty } = useNuxtApp();
const store = useStore();
const dialog = function (content, title, type, duration, width, height, vbind) {
content = typeof content === 'string' ? content : JSON.stringify(content);
let vtitle = type === 'Success' ? `<span class="has-text-primary">${title}</span>` : title;
if (type === 'Error') vtitle = `<span class="has-text-danger">${title}</span>`;
let data = {
id: $id(),
component: `dialog/${type || 'Info'}`,
vbind: { content: content, duration: duration, vbind: vbind },
title: vtitle,
width: width || '600px',
height: height || '100px',
};
store.commit('showmodal', data);
};
const snackbar = function(content, title, type, width, height) {
content = typeof content == 'string'? content : JSON.stringify(content)
let vtitle = type==='Success'? `<span class="has-text-primary">${title}</span>` : title
if(type==='Error') vtitle = `<span class="has-text-danger">${title}</span>`
let data = {id: $id() , component: `snackbar/${type || 'Info'}`,
vbind: {content: content}, title: vtitle, width: width || '600px', height: height || '100px'}
store.commit('snackbar', data)
}
const getLink = function(val) {
if(val === undefined || val === null || val === '' || val==="") return ''
let json = val.indexOf('{')>=0? JSON.parse(val) : {path: val}
return json
}
const snackbar = function (content, title, type, width, height) {
content = typeof content == 'string' ? content : JSON.stringify(content);
let vtitle = type === 'Success' ? `<span class="has-text-primary">${title}</span>` : title;
if (type === 'Error') vtitle = `<span class="has-text-danger">${title}</span>`;
let data = {
id: $id(),
component: `snackbar/${type || 'Info'}`,
vbind: { content: content },
title: vtitle,
width: width || '600px',
height: height || '100px',
};
store.commit('snackbar', data);
};
const errPhone = function(phone) {
var text = undefined
const getLink = function (val) {
if (val === undefined || val === null || val === '' || val === '') return '';
let json = val.indexOf('{') >= 0 ? JSON.parse(val) : { path: val };
return json;
};
const errPhone = function (phone) {
var text = undefined;
if ($empty(phone)) {
text = 'Số điện thoại di động không được bỏ trống.'
text = 'Số điện thoại di động không được bỏ trống.';
} else if (isNaN(phone)) {
text = 'Số điện thoại di động không hợp lệ.'
} else if(phone.length<9 || phone.length>11) {
text = 'Số điện thoại di động phải từ 9-11 số.'
text = 'Số điện thoại di động không hợp lệ.';
} else if (phone.length < 9 || phone.length > 11) {
text = 'Số điện thoại di động phải từ 9-11 số.';
}
return text
}
return text;
};
const errEmail = function(email) {
const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
var text = undefined
const errEmail = function (email) {
const re =
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
var text = undefined;
if ($empty(email)) {
text = 'Email không được bỏ trống.'
} else if (!(re.test(String(email).toLowerCase()))) {
text = 'Email không hợp lệ.'
text = 'Email không được bỏ trống.';
} else if (!re.test(String(email).toLowerCase())) {
text = 'Email không hợp lệ.';
}
return text
}
return text;
};
const errPhoneEmail = function(contact) {
const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
var text = undefined
const errPhoneEmail = function (contact) {
const re =
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
var text = undefined;
if ($empty(contact)) {
text = 'Số điện thoại di động hoặc Email không được bỏ trống.'
text = 'Số điện thoại di động hoặc Email không được bỏ trống.';
} else if (!(re.test(String(contact).toLowerCase()) || !isNaN(contact))) {
text = 'Số điện thoại di động hoặc Email không hợp lệ.'
} else if(!isNaN(contact) && (contact.length<9 || contact.length>11)) {
text = 'Số điện thoại di động không hợp lệ.'
text = 'Số điện thoại di động hoặc Email không hợp lệ.';
} else if (!isNaN(contact) && (contact.length < 9 || contact.length > 11)) {
text = 'Số điện thoại di động không hợp lệ.';
}
return text
}
return text;
};
const upload = function(file, type, user, convert) {
var fileFormat = [{type: 'image', format: ['.png', '.jpg', 'jpeg', '.bmp', '.gif', '.svg', '.webp']},
{type: 'video', format: ['.wmv', '.avi', '.mp4', '.flv', '.mov', '.mpg', '.amv', '.rm']}
]
var valid = undefined
if(type==='image' || type==='video') {
valid = false
let found = fileFormat.find(v=>v.type===type)
found.format.map(x=>{
if(file.name.toLowerCase().indexOf(x)>=0) valid = true
})
const upload = function (file, type, user, convert) {
var fileFormat = [
{ type: 'image', format: ['.png', '.jpg', 'jpeg', '.bmp', '.gif', '.svg', '.webp'] },
{ type: 'video', format: ['.wmv', '.avi', '.mp4', '.flv', '.mov', '.mpg', '.amv', '.rm'] },
];
var valid = undefined;
if (type === 'image' || type === 'video') {
valid = false;
let found = fileFormat.find((v) => v.type === type);
found.format.map((x) => {
if (file.name.toLowerCase().indexOf(x) >= 0) valid = true;
});
}
if(valid===false) return {name: file.name, error: true, text: 'Định dạng file không hợp lệ'}
if((type==='image' || type==='file') && file.size > 500*1024*1024) {
return {name: file.name, error: true, text: 'Kích thước ' + (type==='image'? 'hình ảnh' : 'tài liệu') + ' phải dưới 500MB'}
} else if(type==='video' && file.size > 1073741274) {
return {name: file.name, error: true, text: 'Kích thước video phải dưới 1GB'}
if (valid === false) return { name: file.name, error: true, text: 'Định dạng file không hợp lệ' };
if ((type === 'image' || type === 'file') && file.size > 500 * 1024 * 1024) {
return {
name: file.name,
error: true,
text: 'Kích thước ' + (type === 'image' ? 'hình ảnh' : 'tài liệu') + ' phải dưới 500MB',
};
} else if (type === 'video' && file.size > 1073741274) {
return { name: file.name, error: true, text: 'Kích thước video phải dưới 1GB' };
}
let data = new FormData()
let fileName = dayjs(new Date()).format("YYYYMMDDhhmmss") + '-' + nonAccent(file.name)
data.append('name', file.name)
data.append('filename', fileName)
data.append('file', file)
data.append('type', type)
data.append('size', file.size)
data.append('user', user)
data.append('convert', convert)
return {form: data, type: type, size: file.size, file: file, name: file.name, filename: fileName}
}
let data = new FormData();
let fileName = dayjs(new Date()).format('YYYYMMDDhhmmss') + '-' + nonAccent(file.name);
data.append('name', file.name);
data.append('filename', fileName);
data.append('file', file);
data.append('type', type);
data.append('size', file.size);
data.append('user', user);
data.append('convert', convert);
return { form: data, type: type, size: file.size, file: file, name: file.name, filename: fileName };
};
const checkChange = function(obj1, obj2, list) {
var change = false
if(list) {
list.map(v=>{if(obj1[v]!==obj2[v]) change = true})
const checkChange = function (obj1, obj2, list) {
var change = false;
if (list) {
list.map((v) => {
if (obj1[v] !== obj2[v]) change = true;
});
} else {
for (var k in obj1 ) {
if(obj1[k]!==obj2[k]) change = true
for (var k in obj1) {
if (obj1[k] !== obj2[k]) change = true;
}
}
return change
}
return change;
};
const resetNull = function(obj) {
const resetNull = function (obj) {
for (const [key, value] of Object.entries(obj)) {
if(typeof value==='string') {
let val = value.trim()
if(val==='' || val==="") val = null
obj[key] = val
if (typeof value === 'string') {
let val = value.trim();
if (val === '' || val === '') val = null;
obj[key] = val;
}
}
return obj
}
return obj;
};
const copyToClipboard = function(text) {
snackbar('Copied to clipboard')
const copyToClipboard = function (text) {
snackbar('Copied to clipboard');
if (window.clipboardData && window.clipboardData.setData) {
// IE specific code path to prevent textarea being shown while dialog is visible.
return clipboardData.setData("Text", text);
} else if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
var textarea = document.createElement("textarea");
return clipboardData.setData('Text', text);
} else if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
var textarea = document.createElement('textarea');
textarea.textContent = text;
textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in MS Edge.
textarea.style.position = 'fixed'; // Prevent scrolling to bottom of page in MS Edge.
document.body.appendChild(textarea);
textarea.select();
try {
return document.execCommand("copy"); // Security exception may be thrown by some browsers.
return document.execCommand('copy'); // Security exception may be thrown by some browsers.
} catch (ex) {
console.warn("Copy to clipboard failed.", ex);
console.warn('Copy to clipboard failed.', ex);
return false;
} finally {
document.body.removeChild(textarea);
}
}
}
};
const nonAccent = function(str) {
if($empty(str)) return null
str = str.replaceAll('/', '-').replaceAll('%', '-').replaceAll('?', '-')
const nonAccent = function (str) {
if ($empty(str)) return null;
str = str.replaceAll('/', '-').replaceAll('%', '-').replaceAll('?', '-');
str = str.toLowerCase();
str = str.replace(/à|á|ạ|ả|ã|â|ầ|ấ|ậ|ẩ|ẫ|ă|ằ|ắ|ặ|ẳ|ẵ/g, "a");
str = str.replace(/è|é|ẹ|ẻ|ẽ|ê|ề|ế|ệ|ể|ễ/g, "e");
str = str.replace(/ì|í|ị|ỉ|ĩ/g, "i");
str = str.replace(/ò|ó|ọ|ỏ|õ|ô|ồ|ố|ộ|ổ|ỗ|ơ|ờ|ớ|ợ|ở|ỡ/g, "o");
str = str.replace(/ù|ú|ụ|ủ|ũ|ư|ừ|ứ|ự|ử|ữ/g, "u");
str = str.replace(/ỳ|ý|ỵ|ỷ|ỹ/g, "y");
str = str.replace(/đ/g, "d");
str = str.replace(/à|á|ạ|ả|ã|â|ầ|ấ|ậ|ẩ|ẫ|ă|ằ|ắ|ặ|ẳ|ẵ/g, 'a');
str = str.replace(/è|é|ẹ|ẻ|ẽ|ê|ề|ế|ệ|ể|ễ/g, 'e');
str = str.replace(/ì|í|ị|ỉ|ĩ/g, 'i');
str = str.replace(/ò|ó|ọ|ỏ|õ|ô|ồ|ố|ộ|ổ|ỗ|ơ|ờ|ớ|ợ|ở|ỡ/g, 'o');
str = str.replace(/ù|ú|ụ|ủ|ũ|ư|ừ|ứ|ự|ử|ữ/g, 'u');
str = str.replace(/ỳ|ý|ỵ|ỷ|ỹ/g, 'y');
str = str.replace(/đ/g, 'd');
// Some system encode vietnamese combining accent as individual utf-8 characters
str = str.replace(/\u0300|\u0301|\u0303|\u0309|\u0323/g, ""); // Huyền sắc hỏi ngã nặng
str = str.replace(/\u02C6|\u0306|\u031B/g, ""); // Â, Ê, Ă, Ơ, Ư
str = str.split(' ').filter(s => s).join('-')
return str
}
str = str.replace(/\u0300|\u0301|\u0303|\u0309|\u0323/g, ''); // Huyền sắc hỏi ngã nặng
str = str.replace(/\u02C6|\u0306|\u031B/g, ''); // Â, Ê, Ă, Ơ, Ư
str = str
.split(' ')
.filter((s) => s)
.join('-');
return str;
};
const linkID = function(link) {
link = link? link : route.params.slug
if($empty(link)) return
let idx = link.lastIndexOf('-')
let id = (idx>-1 && idx<link.length-1)? link.substring(idx+1, link.length) : undefined
return id
}
const linkID = function (link) {
link = link ? link : route.params.slug;
if ($empty(link)) return;
let idx = link.lastIndexOf('-');
let id = idx > -1 && idx < link.length - 1 ? link.substring(idx + 1, link.length) : undefined;
return id;
};
const exportpdf = function(docid, name, size) {
var element = document.getElementById(docid)
let elms = element.querySelectorAll("[id='ignore']")
for(var i = 0; i < elms.length; i++) {
elms[i].style.display='none';
const exportpdf = function (docid, name, size) {
var element = document.getElementById(docid);
let elms = element.querySelectorAll("[id='ignore']");
for (var i = 0; i < elms.length; i++) {
elms[i].style.display = 'none';
}
var opt = {
margin: 0.2,
filename: `${name || 'file'}-${dayjs().format('YYYYMMDDHHmmss')}.pdf`,
image: { type: 'jpeg', quality: 1 },
html2canvas: { scale: 2, useCORS: true},
jsPDF: { unit: 'in', format: size || 'a4', orientation: 'portrait' }
}
html2pdf().set(opt).from(element).save()
setTimeout(()=> {
for(var i = 0; i < elms.length; i++) {
elms[i].style.display='block';
margin: 0.2,
filename: `${name || 'file'}-${dayjs().format('YYYYMMDDHHmmss')}.pdf`,
image: { type: 'jpeg', quality: 1 },
html2canvas: { scale: 2, useCORS: true },
jsPDF: { unit: 'in', format: size || 'a4', orientation: 'portrait' },
};
html2pdf().set(opt).from(element).save();
setTimeout(() => {
for (var i = 0; i < elms.length; i++) {
elms[i].style.display = 'block';
}
}, 1000)
return opt.filename
}
}, 1000);
return opt.filename;
};
const exportImage = function(docid, name) {
var element = document.getElementById(docid)
let elms = element.querySelectorAll("[id='ignore']")
for(var i = 0; i < elms.length; i++) {
elms[i].style.display='none';
const exportImage = function (docid, name) {
var element = document.getElementById(docid);
let elms = element.querySelectorAll("[id='ignore']");
for (var i = 0; i < elms.length; i++) {
elms[i].style.display = 'none';
}
let filename = `${name}-${dayjs().format('YYYYMMDDHHmmss')}.png`
html2canvas(element).then(canvas => {
const link = document.createElement("a");
link.href = canvas.toDataURL("image/png");
let filename = `${name}-${dayjs().format('YYYYMMDDHHmmss')}.png`;
html2canvas(element).then((canvas) => {
const link = document.createElement('a');
link.href = canvas.toDataURL('image/png');
link.download = filename;
link.click();
link.remove()
})
setTimeout(()=> {
for(var i = 0; i < elms.length; i++) {
elms[i].style.display='block';
link.remove();
});
setTimeout(() => {
for (var i = 0; i < elms.length; i++) {
elms[i].style.display = 'block';
}
}, 1000)
return filename
}
}, 1000);
return filename;
};
const download = async function(url, fileName) {
let name = dayjs(new Date()).format("YYYYMMDDhhmmss") + '-' + fileName
const response = await fetch(url, { method: "GET" });
const download = async function (url, fileName) {
let name = dayjs(new Date()).format('YYYYMMDDhhmmss') + '-' + fileName;
const response = await fetch(url, { method: 'GET' });
const blob = await response.blob();
const urlDownload = window.URL.createObjectURL(blob);
const link = document.createElement("a");
const link = document.createElement('a');
link.href = urlDownload;
link.setAttribute("download", name);
link.setAttribute('download', name);
link.click();
link.remove();
}
};
const vnmoney = function(amount) {
const vnmoney = function (amount) {
var readGroup = function (group) {
let readDigit = [" Không", " Một", " Hai", " Ba", " Bốn", " Năm", " Sáu", " Bảy", " Tám", " Chín"];
var temp = "";
if (group == "000") return "";
temp = readDigit[parseInt(group.substring(0, 1))] + " Trăm";
if (group.substring(1, 2) == "0")
if (group.substring(2, 3) == "0") return temp;
else {
temp += " Lẻ" + readDigit[parseInt(group.substring(2, 3))];
return temp;
}
else
temp += readDigit[parseInt(group.substring(1, 2))] + " Mươi";
if (group.substring(2, 3) == "5") temp += " Lăm";
else if (group.substring(2, 3) != "0") temp += readDigit[parseInt(group.substring(2, 3))];
let readDigit = [' Không', ' Một', ' Hai', ' Ba', ' Bốn', ' Năm', ' Sáu', ' Bảy', ' Tám', ' Chín'];
var temp = '';
if (group == '000') return '';
temp = readDigit[parseInt(group.substring(0, 1))] + ' Trăm';
if (group.substring(1, 2) == '0')
if (group.substring(2, 3) == '0') return temp;
else {
temp += ' Lẻ' + readDigit[parseInt(group.substring(2, 3))];
return temp;
}
else temp += readDigit[parseInt(group.substring(1, 2))] + ' Mươi';
if (group.substring(2, 3) == '5') temp += ' Lăm';
else if (group.substring(2, 3) != '0') temp += readDigit[parseInt(group.substring(2, 3))];
return temp;
}
var readMoney = function(num) {
if ((num == null) || (num == "")) return "";
let temp = "";
};
var readMoney = function (num) {
if (num == null || num == '') return '';
let temp = '';
while (num.length < 18) {
num = "0" + num;
num = '0' + num;
}
let g1 = num.substring(0, 3);
let g2 = num.substring(3, 6);
@@ -262,110 +284,114 @@ export default defineNuxtPlugin(() => {
let g4 = num.substring(9, 12);
let g5 = num.substring(12, 15);
let g6 = num.substring(15, 18);
if (g1 != "000") {
temp = readGroup(g1);
temp += " Triệu";
if (g1 != '000') {
temp = readGroup(g1);
temp += ' Triệu';
}
if (g2 != "000") {
temp += readGroup(g2);
temp += " Nghìn";
if (g2 != '000') {
temp += readGroup(g2);
temp += ' Nghìn';
}
if (g3 != "000") {
temp += readGroup(g3);
temp += " Tỷ";
} else if ("" != temp) {
temp += " Tỷ";
if (g3 != '000') {
temp += readGroup(g3);
temp += ' Tỷ';
} else if ('' != temp) {
temp += ' Tỷ';
}
if (g4 != "000") {
temp += readGroup(g4);
temp += " Triệu";
if (g4 != '000') {
temp += readGroup(g4);
temp += ' Triệu';
}
if (g5 != "000") {
temp += readGroup(g5);
temp += " Nghìn";
if (g5 != '000') {
temp += readGroup(g5);
temp += ' Nghìn';
}
temp = temp + readGroup(g6);
temp = temp.replaceAll("Một Mươi", "Mười");
temp = temp.replaceAll('Một Mươi', 'Mười');
temp = temp.trim();
temp = temp.replaceAll("Không Trăm", "");
temp = temp.replaceAll('Không Trăm', '');
temp = temp.trim();
temp = temp.replaceAll("Mười Không", "Mười");
temp = temp.replaceAll('Mười Không', 'Mười');
temp = temp.trim();
temp = temp.replaceAll("Mươi Không", "Mươi");
temp = temp.replaceAll('Mươi Không', 'Mươi');
temp = temp.trim();
if (temp.indexOf("Lẻ") == 0) temp = temp.substring(2);
if (temp.indexOf('Lẻ') == 0) temp = temp.substring(2);
temp = temp.trim();
temp = temp.replaceAll("Mươi Một", "Mươi Mốt");
temp = temp.replaceAll('Mươi Một', 'Mươi Mốt');
temp = temp.trim();
let result = temp.substring(0, 1).toUpperCase() + temp.substring(1).toLowerCase();
return (result == "" ? "Không" : result) + " đồng chẵn"
}
return readMoney(amount.toString())
}
return (result == '' ? 'Không' : result) + ' đồng chẵn';
};
return readMoney(amount.toString());
};
const lang = function(code) {
let field = store.common.find((v) => v.code===code)
return field? field[store.lang] : ''
}
const lang = function (code) {
let field = store.common.find((v) => v.code === code);
return field ? field[store.lang] : '';
};
const createMeta = function(metainfo) {
const createMeta = function (metainfo) {
return {
title: metainfo.title,
link: [
{
hid: 'canonical',
rel: 'canonical',
href: `https://y99.vn${route.path}`
}
href: ``,
},
],
meta: [
{
hid: 'description',
name: 'description',
content: metainfo.description || ''
content: metainfo.description || '',
},
{
hid: 'og:title',
property: 'og:title',
content: metainfo.title
content: metainfo.title,
},
{
hid: 'og:description',
property: 'og:description',
content: metainfo.description || ''
content: metainfo.description || '',
},
{
hid: 'og:type',
property: 'og:type',
content: metainfo.type || ''
content: metainfo.type || '',
},
{
hid: 'og:image',
property: 'og:image',
content: metainfo.image || ''
content: metainfo.image || '',
},
{
hid: 'og:url',
property: 'og:url',
content: `https://y99.vn${route.path}`
content: ``,
},
{
property: 'og:locale',
content: 'vi_VN'
content: 'vi_VN',
},
{
hid: 'fb:app_id',
property: 'fb:app_id',
content: metainfo.app_id || '549555567061256'
content: metainfo.app_id || '',
},
{
hid: 'keywords',
name: 'keywords',
content: metainfo.keywords || ''
}
]
}
}
content: metainfo.keywords || '',
},
],
};
};
const isVietnamese = () => {
return store.lang.toLowerCase() === 'vi';
};
return {
provide: {
@@ -387,7 +413,8 @@ export default defineNuxtPlugin(() => {
createMeta,
download,
dayjs,
lang
}
}
})
lang,
isVietnamese,
},
};
});

View File

@@ -449,7 +449,9 @@ export default defineNuxtPlugin(() => {
deleterow,
updatepage,
store,
requestLogin
requestLogin,
mode,
path
}
}
})