Compare commits

...

2 Commits

Author SHA1 Message Date
TangShanDD dec10895b1 fix: 格式化代码 2025-11-06 16:02:06 +08:00
TangShanDD 2b92c69e75 fix: 登录页重构 2025-11-06 16:01:50 +08:00
30 changed files with 590 additions and 255 deletions

6
components.d.ts vendored
View File

@ -7,6 +7,7 @@ declare module 'vue' {
ABadge: typeof import('ant-design-vue/es')['Badge'] ABadge: typeof import('ant-design-vue/es')['Badge']
AButton: typeof import('ant-design-vue/es')['Button'] AButton: typeof import('ant-design-vue/es')['Button']
ACard: typeof import('ant-design-vue/es')['Card'] ACard: typeof import('ant-design-vue/es')['Card']
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
ACol: typeof import('ant-design-vue/es')['Col'] ACol: typeof import('ant-design-vue/es')['Col']
ADropdown: typeof import('ant-design-vue/es')['Dropdown'] ADropdown: typeof import('ant-design-vue/es')['Dropdown']
AEmpty: typeof import('ant-design-vue/es')['Empty'] AEmpty: typeof import('ant-design-vue/es')['Empty']
@ -26,6 +27,11 @@ declare module 'vue' {
ASwitch: typeof import('ant-design-vue/es')['Switch'] ASwitch: typeof import('ant-design-vue/es')['Switch']
ATextarea: typeof import('ant-design-vue/es')['Textarea'] ATextarea: typeof import('ant-design-vue/es')['Textarea']
ATooltip: typeof import('ant-design-vue/es')['Tooltip'] ATooltip: typeof import('ant-design-vue/es')['Tooltip']
ElButton: typeof import('element-plus/es')['ElButton']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElInput: typeof import('element-plus/es')['ElInput']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
Empty: typeof import('./src/components/empty/Empty.vue')['default'] Empty: typeof import('./src/components/empty/Empty.vue')['default']
ImageCropper: typeof import('./src/components/image-cropper/index.vue')['default'] ImageCropper: typeof import('./src/components/image-cropper/index.vue')['default']

View File

@ -12,17 +12,17 @@
export default { export default {
props: { props: {
image: { image: {
type: String, type: String
}, },
icon: { icon: {
type: String, type: String,
default: 'el-icon-tickets', default: 'el-icon-tickets'
}, },
description: { description: {
type: String, type: String,
default: '暂无数据', default: '暂无数据'
}, }
}, }
} }
</script> </script>

View File

@ -14,6 +14,6 @@ export default function (data, mime) {
} }
// canvas.toDataURL 返回的默认格式就是 image/png // canvas.toDataURL 返回的默认格式就是 image/png
return new Blob([ia], { return new Blob([ia], {
type: mime, type: mime
}) })
} }

View File

@ -10,7 +10,7 @@ export default function (e, argOpts) {
{ {
ele: e.target, // 波纹作用元素 ele: e.target, // 波纹作用元素
type: 'hit', // hit点击位置扩散center中心点扩展 type: 'hit', // hit点击位置扩散center中心点扩展
bgc: 'rgba(0, 0, 0, 0.15)', // 波纹颜色 bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
}, },
argOpts argOpts
) )

View File

@ -10,13 +10,13 @@ export default {
off: '取消', off: '取消',
close: '关闭', close: '关闭',
back: '上一步', back: '上一步',
save: '保存', save: '保存'
}, },
error: { error: {
onlyImg: '仅限图片格式', onlyImg: '仅限图片格式',
outOfSize: '单文件大小不能超过 ', outOfSize: '单文件大小不能超过 ',
lowestPx: '图片最低像素为(宽*高):', lowestPx: '图片最低像素为(宽*高):'
}, }
}, },
'zh-tw': { 'zh-tw': {
hint: '點擊,或拖動圖片至此處', hint: '點擊,或拖動圖片至此處',
@ -29,13 +29,13 @@ export default {
off: '取消', off: '取消',
close: '關閉', close: '關閉',
back: '上一步', back: '上一步',
save: '保存', save: '保存'
}, },
error: { error: {
onlyImg: '僅限圖片格式', onlyImg: '僅限圖片格式',
outOfSize: '單文件大小不能超過 ', outOfSize: '單文件大小不能超過 ',
lowestPx: '圖片最低像素為(寬*高):', lowestPx: '圖片最低像素為(寬*高):'
}, }
}, },
en: { en: {
hint: 'Click or drag the file here to upload', hint: 'Click or drag the file here to upload',
@ -48,13 +48,13 @@ export default {
off: 'Cancel', off: 'Cancel',
close: 'Close', close: 'Close',
back: 'Back', back: 'Back',
save: 'Save', save: 'Save'
}, },
error: { error: {
onlyImg: 'Image only', onlyImg: 'Image only',
outOfSize: 'Image exceeds size limit: ', outOfSize: 'Image exceeds size limit: ',
lowestPx: "Image's size is too low. Expected at least: ", lowestPx: "Image's size is too low. Expected at least: "
}, }
}, },
ro: { ro: {
hint: 'Atinge sau trage fișierul aici', hint: 'Atinge sau trage fișierul aici',
@ -68,14 +68,14 @@ export default {
off: 'Anulează', off: 'Anulează',
close: 'Închide', close: 'Închide',
back: 'Înapoi', back: 'Înapoi',
save: 'Salvează', save: 'Salvează'
}, },
error: { error: {
onlyImg: 'Doar imagini', onlyImg: 'Doar imagini',
outOfSize: 'Imaginea depășește limita de: ', outOfSize: 'Imaginea depășește limita de: ',
loewstPx: 'Imaginea este prea mică; Minim: ', loewstPx: 'Imaginea este prea mică; Minim: '
}, }
}, },
ru: { ru: {
hint: 'Нажмите, или перетащите файл в это окно', hint: 'Нажмите, или перетащите файл в это окно',
@ -88,13 +88,13 @@ export default {
off: 'Отменить', off: 'Отменить',
close: 'Закрыть', close: 'Закрыть',
back: 'Назад', back: 'Назад',
save: 'Сохранить', save: 'Сохранить'
}, },
error: { error: {
onlyImg: 'Только изображения', onlyImg: 'Только изображения',
outOfSize: 'Изображение превышает предельный размер: ', outOfSize: 'Изображение превышает предельный размер: ',
lowestPx: 'Минимальный размер изображения: ', lowestPx: 'Минимальный размер изображения: '
}, }
}, },
'pt-br': { 'pt-br': {
hint: 'Clique ou arraste o arquivo aqui para carregar', hint: 'Clique ou arraste o arquivo aqui para carregar',
@ -107,13 +107,13 @@ export default {
off: 'Cancelar', off: 'Cancelar',
close: 'Fechar', close: 'Fechar',
back: 'Voltar', back: 'Voltar',
save: 'Salvar', save: 'Salvar'
}, },
error: { error: {
onlyImg: 'Apenas imagens', onlyImg: 'Apenas imagens',
outOfSize: 'A imagem excede o limite de tamanho: ', outOfSize: 'A imagem excede o limite de tamanho: ',
lowestPx: 'O tamanho da imagem é muito pequeno. Tamanho mínimo: ', lowestPx: 'O tamanho da imagem é muito pequeno. Tamanho mínimo: '
}, }
}, },
fr: { fr: {
hint: 'Cliquez ou glissez le fichier ici.', hint: 'Cliquez ou glissez le fichier ici.',
@ -126,13 +126,13 @@ export default {
off: 'Annuler', off: 'Annuler',
close: 'Fermer', close: 'Fermer',
back: 'Retour', back: 'Retour',
save: 'Enregistrer', save: 'Enregistrer'
}, },
error: { error: {
onlyImg: 'Image uniquement', onlyImg: 'Image uniquement',
outOfSize: "L'image sélectionnée dépasse la taille maximum: ", outOfSize: "L'image sélectionnée dépasse la taille maximum: ",
lowestPx: "L'image sélectionnée est trop petite. Dimensions attendues: ", lowestPx: "L'image sélectionnée est trop petite. Dimensions attendues: "
}, }
}, },
nl: { nl: {
hint: 'Klik hier of sleep een afbeelding in dit vlak', hint: 'Klik hier of sleep een afbeelding in dit vlak',
@ -145,13 +145,13 @@ export default {
off: 'Annuleren', off: 'Annuleren',
close: 'Sluiten', close: 'Sluiten',
back: 'Terug', back: 'Terug',
save: 'Opslaan', save: 'Opslaan'
}, },
error: { error: {
onlyImg: 'Alleen afbeeldingen', onlyImg: 'Alleen afbeeldingen',
outOfSize: 'De afbeelding is groter dan: ', outOfSize: 'De afbeelding is groter dan: ',
lowestPx: 'De afbeelding is te klein! Minimale afmetingen: ', lowestPx: 'De afbeelding is te klein! Minimale afmetingen: '
}, }
}, },
tr: { tr: {
hint: 'Tıkla veya yüklemek istediğini buraya sürükle', hint: 'Tıkla veya yüklemek istediğini buraya sürükle',
@ -164,13 +164,13 @@ export default {
off: 'İptal', off: 'İptal',
close: 'Kapat', close: 'Kapat',
back: 'Geri', back: 'Geri',
save: 'Kaydet', save: 'Kaydet'
}, },
error: { error: {
onlyImg: 'Sadece resim', onlyImg: 'Sadece resim',
outOfSize: 'Resim yükleme limitini aşıyor: ', outOfSize: 'Resim yükleme limitini aşıyor: ',
lowestPx: 'Resmin boyutu çok küçük. En az olması gereken: ', lowestPx: 'Resmin boyutu çok küçük. En az olması gereken: '
}, }
}, },
'es-MX': { 'es-MX': {
hint: 'Selecciona o arrastra una imagen', hint: 'Selecciona o arrastra una imagen',
@ -183,13 +183,13 @@ export default {
off: 'Cancelar', off: 'Cancelar',
close: 'Cerrar', close: 'Cerrar',
back: 'Atrás', back: 'Atrás',
save: 'Guardar', save: 'Guardar'
}, },
error: { error: {
onlyImg: 'Únicamente imágenes', onlyImg: 'Únicamente imágenes',
outOfSize: 'La imagen excede el tamaño maximo:', outOfSize: 'La imagen excede el tamaño maximo:',
lowestPx: 'La imagen es demasiado pequeña. Se espera por lo menos:', lowestPx: 'La imagen es demasiado pequeña. Se espera por lo menos:'
}, }
}, },
de: { de: {
hint: 'Klick hier oder zieh eine Datei hier rein zum Hochladen', hint: 'Klick hier oder zieh eine Datei hier rein zum Hochladen',
@ -202,13 +202,13 @@ export default {
off: 'Abbrechen', off: 'Abbrechen',
close: 'Schließen', close: 'Schließen',
back: 'Zurück', back: 'Zurück',
save: 'Speichern', save: 'Speichern'
}, },
error: { error: {
onlyImg: 'Nur Bilder', onlyImg: 'Nur Bilder',
outOfSize: 'Das Bild ist zu groß: ', outOfSize: 'Das Bild ist zu groß: ',
lowestPx: 'Das Bild ist zu klein. Mindestens: ', lowestPx: 'Das Bild ist zu klein. Mindestens: '
}, }
}, },
ja: { ja: {
hint: 'クリック・ドラッグしてファイルをアップロード', hint: 'クリック・ドラッグしてファイルをアップロード',
@ -221,13 +221,13 @@ export default {
off: 'キャンセル', off: 'キャンセル',
close: '閉じる', close: '閉じる',
back: '戻る', back: '戻る',
save: '保存', save: '保存'
}, },
error: { error: {
onlyImg: '画像のみ', onlyImg: '画像のみ',
outOfSize: '画像サイズが上限を超えています。上限: ', outOfSize: '画像サイズが上限を超えています。上限: ',
lowestPx: '画像が小さすぎます。最小サイズ: ', lowestPx: '画像が小さすぎます。最小サイズ: '
}, }
}, },
ua: { ua: {
hint: 'Натисніть, або перетягніть файл в це вікно', hint: 'Натисніть, або перетягніть файл в це вікно',
@ -240,13 +240,13 @@ export default {
off: 'Відмінити', off: 'Відмінити',
close: 'Закрити', close: 'Закрити',
back: 'Назад', back: 'Назад',
save: 'Зберегти', save: 'Зберегти'
}, },
error: { error: {
onlyImg: 'Тільки зображення', onlyImg: 'Тільки зображення',
outOfSize: 'Зображення перевищує граничний розмір: ', outOfSize: 'Зображення перевищує граничний розмір: ',
lowestPx: 'Мінімальний розмір зображення: ', lowestPx: 'Мінімальний розмір зображення: '
}, }
}, },
it: { it: {
hint: 'Clicca o trascina qui il file per caricarlo', hint: 'Clicca o trascina qui il file per caricarlo',
@ -259,13 +259,13 @@ export default {
off: 'Annulla', off: 'Annulla',
close: 'Chiudi', close: 'Chiudi',
back: 'Indietro', back: 'Indietro',
save: 'Salva', save: 'Salva'
}, },
error: { error: {
onlyImg: 'Sono accettate solo immagini', onlyImg: 'Sono accettate solo immagini',
outOfSize: "L'immagine eccede i limiti di dimensione: ", outOfSize: "L'immagine eccede i limiti di dimensione: ",
lowestPx: "L'immagine è troppo piccola. Il requisito minimo è: ", lowestPx: "L'immagine è troppo piccola. Il requisito minimo è: "
}, }
}, },
ar: { ar: {
hint: 'اضغط أو اسحب الملف هنا للتحميل', hint: 'اضغط أو اسحب الملف هنا للتحميل',
@ -278,13 +278,13 @@ export default {
off: 'إلغاء', off: 'إلغاء',
close: 'إغلاق', close: 'إغلاق',
back: 'رجوع', back: 'رجوع',
save: 'حفظ', save: 'حفظ'
}, },
error: { error: {
onlyImg: 'صور فقط', onlyImg: 'صور فقط',
outOfSize: 'تتجاوز الصوره الحجم المحدد: ', outOfSize: 'تتجاوز الصوره الحجم المحدد: ',
lowestPx: 'حجم الصورة صغير جدا. من المتوقع على الأقل: ', lowestPx: 'حجم الصورة صغير جدا. من المتوقع على الأقل: '
}, }
}, },
ug: { ug: {
hint: 'مەزكۇر دائىرىنى چىكىپ رەسىم تاللاڭ ياكى رەسىمنى سۆرەپ ئەكىرىڭ', hint: 'مەزكۇر دائىرىنى چىكىپ رەسىم تاللاڭ ياكى رەسىمنى سۆرەپ ئەكىرىڭ',
@ -297,13 +297,13 @@ export default {
off: 'بولدى قىلىش', off: 'بولدى قىلىش',
close: 'تاقاش', close: 'تاقاش',
back: 'ئالدىنقى قەدەم', back: 'ئالدىنقى قەدەم',
save: 'ساقلاش', save: 'ساقلاش'
}, },
error: { error: {
onlyImg: 'پەقەت رەسىم فورماتىنىلا قوللايدۇ', onlyImg: 'پەقەت رەسىم فورماتىنىلا قوللايدۇ',
outOfSize: 'رەسىم چوڭ - كىچىكلىكى چەكتىن ئىشىپ كەتتى', outOfSize: 'رەسىم چوڭ - كىچىكلىكى چەكتىن ئىشىپ كەتتى',
lowestPx: 'رەسىمنىڭ ئەڭ كىچىك ئۆلچىمى :', lowestPx: 'رەسىمنىڭ ئەڭ كىچىك ئۆلچىمى :'
}, }
}, },
th: { th: {
hint: 'คลิ๊กหรือลากรูปมาที่นี่', hint: 'คลิ๊กหรือลากรูปมาที่นี่',
@ -316,13 +316,13 @@ export default {
off: 'ยกเลิก', off: 'ยกเลิก',
close: 'ปิด', close: 'ปิด',
back: 'กลับ', back: 'กลับ',
save: 'บันทึก', save: 'บันทึก'
}, },
error: { error: {
onlyImg: 'ไฟล์ภาพเท่านั้น', onlyImg: 'ไฟล์ภาพเท่านั้น',
outOfSize: 'ไฟล์ใหญ่เกินกำหนด: ', outOfSize: 'ไฟล์ใหญ่เกินกำหนด: ',
lowestPx: 'ไฟล์เล็กเกินไป. อย่างน้อยต้องมีขนาด: ', lowestPx: 'ไฟล์เล็กเกินไป. อย่างน้อยต้องมีขนาด: '
}, }
}, },
mm: { mm: {
hint: 'ဖိုင်ကို ဤနေရာတွင် နှိပ်၍ (သို့) ဆွဲထည့်၍ တင်ပါ', hint: 'ဖိုင်ကို ဤနေရာတွင် နှိပ်၍ (သို့) ဆွဲထည့်၍ တင်ပါ',
@ -335,13 +335,13 @@ export default {
off: 'မလုပ်တော့ပါ', off: 'မလုပ်တော့ပါ',
close: 'ပိတ်မည်', close: 'ပိတ်မည်',
back: 'နောက်သို့', back: 'နောက်သို့',
save: 'သိမ်းမည်', save: 'သိမ်းမည်'
}, },
error: { error: {
onlyImg: 'ဓာတ်ပုံ သီးသန့်သာ', onlyImg: 'ဓာတ်ပုံ သီးသန့်သာ',
outOfSize: 'ဓာတ်ပုံဆိုဒ် ကြီးလွန်းသည် ။ အများဆုံး ဆိုဒ် : ', outOfSize: 'ဓာတ်ပုံဆိုဒ် ကြီးလွန်းသည် ။ အများဆုံး ဆိုဒ် : ',
lowestPx: 'ဓာတ်ပုံဆိုဒ် သေးလွန်းသည်။ အနည်းဆုံး ဆိုဒ် : ', lowestPx: 'ဓာတ်ပုံဆိုဒ် သေးလွန်းသည်။ အနည်းဆုံး ဆိုဒ် : '
}, }
}, },
se: { se: {
hint: 'Klicka eller dra en fil hit för att ladda upp den', hint: 'Klicka eller dra en fil hit för att ladda upp den',
@ -354,12 +354,12 @@ export default {
off: 'Avbryt', off: 'Avbryt',
close: 'Stäng', close: 'Stäng',
back: 'Tillbaka', back: 'Tillbaka',
save: 'Spara', save: 'Spara'
}, },
error: { error: {
onlyImg: 'Endast bilder', onlyImg: 'Endast bilder',
outOfSize: 'Bilden är större än max-gränsen: ', outOfSize: 'Bilden är större än max-gränsen: ',
lowestPx: 'Bilden är för liten. Minimum är: ', lowestPx: 'Bilden är för liten. Minimum är: '
}, }
}, }
} }

View File

@ -3,5 +3,5 @@ export default {
png: 'image/png', png: 'image/png',
gif: 'image/gif', gif: 'image/gif',
svg: 'image/svg+xml', svg: 'image/svg+xml',
psd: 'image/photoshop', psd: 'image/photoshop'
} }

View File

@ -12,15 +12,15 @@ export default defineComponent({
props: { props: {
iconName: { iconName: {
type: String, type: String,
required: true, required: true
}, }
}, },
setup(props) { setup(props) {
const icon = computed(() => `#icon-${props.iconName}`) const icon = computed(() => `#icon-${props.iconName}`)
return { return {
icon, icon
} }
}, }
}) })
</script> </script>

View File

@ -6,6 +6,6 @@ export default function () {
} }
return { return {
selectionIds, selectionIds,
handleSelectionChange, handleSelectionChange
} }
} }

View File

@ -41,7 +41,7 @@ export function useDelete(removeService: IRemoveService, getData: { (): void },
// }) // })
} }
return { return {
handleDelete, handleDelete
} }
} }
export default function <T = any>(configs: IConfigs) { export default function <T = any>(configs: IConfigs) {
@ -53,8 +53,8 @@ export default function <T = any>(configs: IConfigs) {
params: { params: {
page: 1, page: 1,
rows, rows,
...params, ...params
}, }
}) })
if (initParams) { if (initParams) {
state.params.params = handleSearchParam(initParams) state.params.params = handleSearchParam(initParams)
@ -82,6 +82,6 @@ export default function <T = any>(configs: IConfigs) {
loading, loading,
...toRefs(state), ...toRefs(state),
getList, getList,
handleDelete, handleDelete
} }
} }

View File

@ -2,6 +2,6 @@
export default { export default {
render: function (h) { render: function (h) {
return h() return h()
}, }
} }
</script> </script>

View File

@ -47,9 +47,9 @@ export default {
clearInterval(timer) clearInterval(timer)
}) })
return { return {
tipText, tipText
} }
}, }
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -11,7 +11,7 @@
<script> <script>
export default { export default {
props: { props: {
pageConfigs: Object, pageConfigs: Object
}, },
watch: { watch: {
firstPath() { firstPath() {
@ -30,8 +30,8 @@ export default {
handler: function () { handler: function () {
this.initMenu('menuData') this.initMenu('menuData')
}, },
immediate: true, immediate: true
}, }
}, },
computed: { computed: {
firstPath: function () { firstPath: function () {
@ -42,7 +42,7 @@ export default {
}, },
hiddenSide() { hiddenSide() {
return this.$route.meta.hiddenSide return this.$route.meta.hiddenSide
}, }
}, },
created() {}, created() {},
methods: { methods: {
@ -50,7 +50,7 @@ export default {
if (item.selected) { if (item.selected) {
return { return {
backgroundColor: this.pageConfigs.headerSelectColour, backgroundColor: this.pageConfigs.headerSelectColour,
color: this.pageConfigs.headerFontSelectColour, color: this.pageConfigs.headerFontSelectColour
} }
} }
}, },
@ -99,8 +99,8 @@ export default {
} else { } else {
this.resetMenu(1) this.resetMenu(1)
} }
}, }
}, }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -48,7 +48,7 @@ export default defineComponent({
'lockData', 'lockData',
JSON.stringify({ JSON.stringify({
path: route.fullPath, path: route.fullPath,
isLock: true, isLock: true
}) })
) )
router.push({ name: 'LockMe' }) router.push({ name: 'LockMe' })
@ -81,9 +81,9 @@ export default defineComponent({
}) })
onUnmounted(clearTimer) onUnmounted(clearTimer)
return { return {
lockScreen, lockScreen
} }
}, }
}) })
</script> </script>
<style scoped> <style scoped>

View File

@ -13,16 +13,16 @@
export default { export default {
props: { props: {
path: { path: {
type: String, type: String
}, },
meta: { meta: {
type: Object, type: Object
}, },
defaultIcon: { defaultIcon: {
type: String, type: String,
default: '', default: ''
}, }
}, }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -40,6 +40,6 @@ export default function (props: { mainMenu: boolean }) {
} }
return { return {
openKeys, openKeys,
handleOpenChange, handleOpenChange
} }
} }

View File

@ -1,12 +1,12 @@
export default { export default {
data() { data() {
return { return {
selectionIds: [], selectionIds: []
} }
}, },
methods: { methods: {
handleSelectionChange(selections) { handleSelectionChange(selections) {
this.selectionIds = selections.map((item) => item.id) this.selectionIds = selections.map((item) => item.id)
}, }
}, }
} }

View File

@ -4,7 +4,7 @@ import { getToken } from 'utils/auth'
export default { export default {
data() { data() {
return { return {
webSocket: '', webSocket: ''
} }
}, },
created() { created() {
@ -14,7 +14,7 @@ export default {
commonFun: this.messageCommonFun, commonFun: this.messageCommonFun,
pingMsg: 'HeartBeat', pingMsg: 'HeartBeat',
reConnectNum: 5, reConnectNum: 5,
params: getToken(), params: getToken()
}) })
if (this.onmessage && typeof this.onmessage === 'function') { if (this.onmessage && typeof this.onmessage === 'function') {
this.webSocket.onmessage = this.onmessage this.webSocket.onmessage = this.onmessage
@ -25,6 +25,6 @@ export default {
this.webSocket = null this.webSocket = null
}, },
methods: { methods: {
messageCommonFun() {}, messageCommonFun() {}
}, }
} }

View File

@ -4,13 +4,13 @@ import routes from './constant'
function createRoute() { function createRoute() {
return createRouter({ return createRouter({
history: createWebHistory(), history: createWebHistory(),
routes, routes
}) })
} }
const router = createRoute() const router = createRoute()
export const asyncRouterMap = { export const asyncRouterMap = {
Home: () => import('@/layouts/home.vue'), Home: () => import('@/layouts/home.vue'),
App: () => import('@/layouts/app.vue'), App: () => import('@/layouts/app.vue')
} }
export function resetRouter() { export function resetRouter() {
// const newRouter = createRoute(); // const newRouter = createRoute();

View File

@ -21,12 +21,12 @@ export function logout() {
} }
export function getConfig(params) { export function getConfig(params) {
return request.get('/sms/v1/logo', { return request.get('/sms/v1/logo', {
params: wrapperParams(params), params: wrapperParams(params)
}) })
} }
export function getDict(data) { export function getDict(data) {
return request.get('/dict/children', { return request.get('/dict/children', {
params: wrapperParams(data), params: wrapperParams(data)
}) })
} }
export function getSysconf() { export function getSysconf() {
@ -48,7 +48,7 @@ export function getPortal(params) {
} }
export function getServiceQuota(tenantId, data) { export function getServiceQuota(tenantId, data) {
return request.get(`/cos/v1/tenants/${tenantId}/quotas`, { return request.get(`/cos/v1/tenants/${tenantId}/quotas`, {
params: wrapperParams(data), params: wrapperParams(data)
}) })
} }
export function replaceToken(params) { export function replaceToken(params) {
@ -69,7 +69,7 @@ export function getSystemTreeConfigs(params) {
// 更新系统配置信息 // 更新系统配置信息
export function updateSystemConfigs(params) { export function updateSystemConfigs(params) {
return request.put('/sms/v1/system-configs', params, { return request.put('/sms/v1/system-configs', params, {
headers: { 'Content-Type': 'multipart/form-data', BsmAjaxHeader: true, options: { noSeri: true } }, headers: { 'Content-Type': 'multipart/form-data', BsmAjaxHeader: true, options: { noSeri: true } }
}) })
} }
// 测试连接 // 测试连接

View File

@ -7,7 +7,7 @@ const baseUrl = '/sms/v1/config'
export function getStatus(params) { export function getStatus(params) {
return request.get(`${baseUrl}/status`, { return request.get(`${baseUrl}/status`, {
params, params
}) })
} }
export function getSid() { export function getSid() {
@ -15,7 +15,7 @@ export function getSid() {
} }
export function getServer(params) { export function getServer(params) {
return request.get(`${baseUrl}/servers`, { return request.get(`${baseUrl}/servers`, {
params, params
}) })
} }
export function getLicense() { export function getLicense() {

View File

@ -9,7 +9,7 @@ import type { IUser } from '@/models/user'
const baseUrl = '/sms/v1/users' const baseUrl = '/sms/v1/users'
export function getUser(params: Base.IListParams) { export function getUser(params: Base.IListParams) {
return request.get<Base.IListData<IUser>>(baseUrl, { return request.get<Base.IListData<IUser>>(baseUrl, {
params, params
}) })
} }
export function getUserDetail(id: number) { export function getUserDetail(id: number) {
@ -48,7 +48,7 @@ export function getRolesByUser(id: number) {
} }
export function getTrack(params: Base.IListParams) { export function getTrack(params: Base.IListParams) {
return request.get(`${baseUrl}/track`, { return request.get(`${baseUrl}/track`, {
params, params
}) })
} }
export function exportUser(params: Base.IListParams) { export function exportUser(params: Base.IListParams) {

View File

@ -6,7 +6,7 @@ import request from 'utils/request'
const baseUrl = '/sms/v1/messages' const baseUrl = '/sms/v1/messages'
export function getMessage(params) { export function getMessage(params) {
return request.get(baseUrl, { return request.get(baseUrl, {
params, params
}) })
} }
export function getMessageStats() { export function getMessageStats() {

View File

@ -7,7 +7,7 @@ import { wrapperParams } from 'utils/index'
const baseUrl = '/sms/v1/users' const baseUrl = '/sms/v1/users'
export function getUser(params) { export function getUser(params) {
return request.get(baseUrl, { return request.get(baseUrl, {
params, params
}) })
} }
export function getUserDetail(id) { export function getUserDetail(id) {
@ -32,7 +32,7 @@ export function getManager(id) {
export function operateUser(id, action, params) { export function operateUser(id, action, params) {
return request.patch(`${baseUrl}/${id}`, { return request.patch(`${baseUrl}/${id}`, {
action, action,
...wrapperParams(params), ...wrapperParams(params)
}) })
} }
export function checkStatus(data) { export function checkStatus(data) {
@ -45,6 +45,6 @@ export function getRolesByUser(id) {
} }
export function getTrack(params) { export function getTrack(params) {
return request.get(`${baseUrl}/track`, { return request.get(`${baseUrl}/track`, {
params, params
}) })
} }

View File

@ -3,6 +3,6 @@ const getters = {
userData: (state) => state.app.userData, userData: (state) => state.app.userData,
pageConfig: (state) => state.app.pageConfig, pageConfig: (state) => state.app.pageConfig,
systemConfig: (state) => state.app.systemConfig, systemConfig: (state) => state.app.systemConfig,
appLoading: (state) => state.app.appLoading, appLoading: (state) => state.app.appLoading
} }
export default getters export default getters

View File

@ -1,12 +1,12 @@
const state = { const state = {
visitedViews: [], visitedViews: [],
cachedViews: [], cachedViews: []
} }
const mutations = { const mutations = {
ADD_VISITED_VIEW: (state, view) => { ADD_VISITED_VIEW: (state, view) => {
const { const {
meta: { noTag, title }, meta: { noTag, title }
} = view } = view
if (noTag || !title) return if (noTag || !title) return
if (state.visitedViews.some((v) => v.path === view.path)) return if (state.visitedViews.some((v) => v.path === view.path)) return
@ -64,7 +64,7 @@ const mutations = {
}, },
DEL_ALL_CACHED_VIEWS: (state) => { DEL_ALL_CACHED_VIEWS: (state) => {
state.cachedViews = [] state.cachedViews = []
}, }
} }
const actions = { const actions = {
@ -78,7 +78,7 @@ const actions = {
commit('DEL_CACHED_VIEW', view) commit('DEL_CACHED_VIEW', view)
resolve({ resolve({
visitedViews: [...state.visitedViews], visitedViews: [...state.visitedViews],
cachedViews: [...state.cachedViews], cachedViews: [...state.cachedViews]
}) })
}) })
}, },
@ -88,7 +88,7 @@ const actions = {
commit('DEL_OTHERS_CACHED_VIEWS', view) commit('DEL_OTHERS_CACHED_VIEWS', view)
resolve({ resolve({
visitedViews: [...state.visitedViews], visitedViews: [...state.visitedViews],
cachedViews: [...state.cachedViews], cachedViews: [...state.cachedViews]
}) })
}) })
}, },
@ -98,7 +98,7 @@ const actions = {
commit('UPDATE_CACHE_VIEWS') commit('UPDATE_CACHE_VIEWS')
resolve({ resolve({
visitedViews: [...state.visitedViews], visitedViews: [...state.visitedViews],
cachedViews: [...state.cachedViews], cachedViews: [...state.cachedViews]
}) })
}) })
}, },
@ -108,7 +108,7 @@ const actions = {
commit('UPDATE_CACHE_VIEWS') commit('UPDATE_CACHE_VIEWS')
resolve({ resolve({
visitedViews: [...state.visitedViews], visitedViews: [...state.visitedViews],
cachedViews: [...state.cachedViews], cachedViews: [...state.cachedViews]
}) })
}) })
}, },
@ -118,15 +118,15 @@ const actions = {
commit('DEL_ALL_CACHED_VIEWS') commit('DEL_ALL_CACHED_VIEWS')
resolve({ resolve({
visitedViews: [...state.visitedViews], visitedViews: [...state.visitedViews],
cachedViews: [...state.cachedViews], cachedViews: [...state.cachedViews]
}) })
}) })
}, }
} }
export default { export default {
namespaced: true, namespaced: true,
state, state,
mutations, mutations,
actions, actions
} }

View File

@ -21,7 +21,7 @@ const iv = CryptoJS.enc.Utf8.parse(decryptByBase64('QmV5b25kQ01QVjU4NyEhIQ=='))
const options = { const options = {
iv, iv,
mode: CryptoJS.mode.CBC, mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7, padding: CryptoJS.pad.Pkcs7
} }
// 加密方法 // 加密方法
@ -41,5 +41,5 @@ export default {
encrypt, encrypt,
decrypt, decrypt,
encryptByBase64, encryptByBase64,
decryptByBase64, decryptByBase64
} }

View File

@ -15,7 +15,7 @@ export default function uploadFile(item, callback, errorCallBack) {
socket.send( socket.send(
JSON.stringify({ JSON.stringify({
filename: item.file.name, filename: item.file.name,
upload: 'file', upload: 'file'
}) })
) )
// 取消上传 // 取消上传
@ -23,7 +23,7 @@ export default function uploadFile(item, callback, errorCallBack) {
item.progress = 0 item.progress = 0
socket.send( socket.send(
JSON.stringify({ JSON.stringify({
UPLOAD_CANCEL: 'UPLOAD_CANCEL', UPLOAD_CANCEL: 'UPLOAD_CANCEL'
}) })
) )
item.isUploading = false item.isUploading = false
@ -56,7 +56,7 @@ export default function uploadFile(item, callback, errorCallBack) {
item.progress = 100 item.progress = 100
socket.send( socket.send(
JSON.stringify({ JSON.stringify({
sendover: 'sendover', sendover: 'sendover'
}) })
) )
} }

View File

@ -46,9 +46,9 @@ export default defineComponent({
setup() { setup() {
const state = reactive({ const state = reactive({
loginForm: { loginForm: {
password: '', password: ''
}, },
loading: false, loading: false
}) })
let timer: any = 0 let timer: any = 0
const store = useStore() const store = useStore()
@ -56,14 +56,14 @@ export default defineComponent({
// //
const currentTime = ref({ const currentTime = ref({
time: '', time: '',
date: '', date: ''
}) })
function setTimer() { function setTimer() {
const getTime = () => { const getTime = () => {
const time = dayjs() const time = dayjs()
currentTime.value = { currentTime.value = {
time: time.format('HH:mm:ss'), time: time.format('HH:mm:ss'),
date: `${time.format('MM月DD日')}, ${time.format('dddd')}`, date: `${time.format('MM月DD日')}, ${time.format('dddd')}`
} }
} }
getTime() getTime()
@ -73,7 +73,7 @@ export default defineComponent({
} }
let lockData = { let lockData = {
isLock: false, isLock: false,
path: '', path: ''
} }
onMounted(() => { onMounted(() => {
const lockDataItem = localStorage.getItem('lockData') const lockDataItem = localStorage.getItem('lockData')
@ -83,7 +83,7 @@ export default defineComponent({
'lockData', 'lockData',
JSON.stringify({ JSON.stringify({
...lockData, ...lockData,
isLock: true, isLock: true
}) })
) )
setTimer() setTimer()
@ -103,7 +103,7 @@ export default defineComponent({
const res = await login({ const res = await login({
account: userData.value.account, account: userData.value.account,
password: encrypt(values.password), password: encrypt(values.password),
isManager: true, isManager: true
}) })
if (res.success) { if (res.success) {
setLoginData(res.data) setLoginData(res.data)
@ -113,7 +113,7 @@ export default defineComponent({
'lockData', 'lockData',
JSON.stringify({ JSON.stringify({
...lockData, ...lockData,
isLock: false, isLock: false
}) })
) )
} }
@ -129,9 +129,9 @@ export default defineComponent({
loginFormRef, loginFormRef,
required, required,
switchUser, switchUser,
handleLogin, handleLogin
} }
}, }
}) })
</script> </script>

View File

@ -1,42 +1,35 @@
<template> <template>
<div class="login-container" :style="{ backgroundImage: `url(${configs.loginBg || '/web-common-resource/img/bg_login.png'})` }"> <div class="login-container" :style="{ backgroundImage: `url(${configs.loginBg || '/web-common-resource/img/bg_login.png'})` }">
<div class="login-logo"> <div class="login-content">
<img :src="configs.loginLogo" alt /> <!-- 左侧蓝色面板 -->
</div> <div class="left-panel">
<div class="login-center"> <span class="title-text">多云管理平台</span>
<div class="desc-content"> </div>
<span class="desc-title">{{ configs.promotionalTitle }}</span>
<span class="desc-remark">{{ configs.promotionalContent }}</span> <!-- 右侧白色登录表单 -->
<div class="right-panel">
<div class="right-panel-content">
<div class="login-title">欢迎登录</div>
<el-form :model="loginForm" ref="loginFormRef" label-position="top" class="login-form" @keyup.enter="handleLogin">
<el-form-item class="login-form-item" prop="account" label="账号" :rules="[{ ...required, message: '请输入账户' }]">
<el-input v-model="loginForm.account" autocomplete="off" placeholder="请输入账户" size="large" />
</el-form-item>
<el-form-item class="login-form-item" prop="password" label="密码" :rules="[{ ...required, message: '请输入密码' }]">
<el-input v-model="loginForm.password" type="password" show-password placeholder="请输入密码" autocomplete="off" size="large" @blur="capsTooltip = false" @keyup="checkCapslock" />
</el-form-item>
<div class="form-options">
<div class="remember-password">
<el-checkbox v-model="remember"></el-checkbox>
</div>
<a :href="`mailto:${configs.helpInformationLink}`" class="help-link">{{ configs.helpInformationContent || '获取帮助' }}</a>
</div>
<el-button class="login-btn" type="primary" size="large" :loading="loading" @click="handleLogin"></el-button>
</el-form>
</div>
</div> </div>
<a-form :model="loginForm" ref="loginFormRef" label-position="left" label-width="0px" class="card-box login-form" @keyup.enter="handleLogin">
<div class="login-title">账号登录</div>
<a-form-item class="login-form-item" name="account" :rules="[{ ...required, message: '请输入用户名' }]">
<a-input v-model:value="loginForm.account" autocomplete="off" placeholder="登录账户">
<template #prefix>
<UserOutlined />
</template>
</a-input>
</a-form-item>
<a-tooltip v-model="capsTooltip" content="大写锁定已打开" placement="right" manual>
<a-form-item class="login-form-item" name="password" :rules="[{ ...required, message: '请输入密码' }]">
<a-input-password name="password" prefix-icon="el-icon-lock" v-model:value="loginForm.password" placeholder="密码" autocomplete="false" @blur="capsTooltip = false" @keyup="checkCapslock">
<template #prefix>
<LockOutlined />
</template>
</a-input-password>
</a-form-item>
</a-tooltip>
<a-form-item class="login-form-item">
<div class="operate-region">
<span class="remember">
<a-switch v-model:checked="remember"></a-switch>
<span class="m-l-xs">记住密码</span>
</span>
<a :href="`mailto:${configs.helpInformationLink}`" type="text" class="text-info pull-right help-info" :title="configs.helpInformationContent">{{ configs.helpInformationContent }}</a>
</div>
</a-form-item>
<a-button class="login-btn" type="primary" size="medium" :loading="loading" @click="handleLogin"></a-button>
</a-form>
</div> </div>
<div class="copyright-info">{{ configs.copyrightInformation }}</div> <div class="copyright-info">{{ configs.copyrightInformation }}</div>
</div> </div>
@ -46,23 +39,23 @@
import { decrypt, encrypt } from 'utils/crypto' import { decrypt, encrypt } from 'utils/crypto'
import { login } from 'services' import { login } from 'services'
import { reactive, toRefs, ref, computed } from 'vue' import { reactive, toRefs, ref, computed } from 'vue'
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
import { useStore } from 'vuex' import { useStore } from 'vuex'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import setLoginData from './tools' import setLoginData from './tools'
import { required } from '@/validate' import { required } from '@/validate'
export default { export default {
components: { UserOutlined, LockOutlined }, name: 'LoginPage',
setup() { setup() {
const state = reactive({ const state = reactive({
remember: false, remember: false,
userType: 'tenant', // 'tenant' 'user'
loginForm: { loginForm: {
account: '', account: '',
password: '', password: ''
}, },
loading: false, loading: false,
capsTooltip: false, capsTooltip: false
}) })
const store = useStore() const store = useStore()
const configs = computed(() => store.getters.pageConfig) const configs = computed(() => store.getters.pageConfig)
@ -84,7 +77,7 @@ export default {
if (state.remember) { if (state.remember) {
const obj = { const obj = {
account: state.loginForm.account, account: state.loginForm.account,
password: encrypt(state.loginForm.password), password: encrypt(state.loginForm.password)
} }
localStorage.setItem('cmcLoginData', JSON.stringify(obj)) localStorage.setItem('cmcLoginData', JSON.stringify(obj))
} else { } else {
@ -99,17 +92,19 @@ export default {
async function handleLogin() { async function handleLogin() {
try { try {
state.loading = true state.loading = true
const values = await loginFormRef.value.validate() await loginFormRef.value.validate()
const { account, password } = values const { account, password } = state.loginForm
const res = await login({ const res = await login({
account, account,
password: encrypt(password), password: encrypt(password),
isManager: true, isManager: true
}) })
if (res.success) { if (res.success) {
goLogin(res.data) goLogin(res.data)
} }
} catch (error) {} } catch (error) {
//
}
state.loading = false state.loading = false
} }
@ -121,7 +116,7 @@ export default {
state.capsTooltip = false state.capsTooltip = false
} }
} }
if (key === 'CapsLock' && this.capsTooltip === true) { if (key === 'CapsLock' && state.capsTooltip === true) {
state.capsTooltip = false state.capsTooltip = false
} }
} }
@ -131,107 +126,204 @@ export default {
configs, configs,
loginFormRef, loginFormRef,
handleLogin, handleLogin,
checkCapslock, checkCapslock
} }
}, }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.login-container { .login-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100vh; height: 100vh;
position: relative;
background: url('/web-common-resource/img/bg_login.png') #2d3a4b no-repeat; background: url('/web-common-resource/img/bg_login.png') #2d3a4b no-repeat;
background-size: cover; background-size: cover;
.login-logo { .login-content {
padding: 5px 40px;
img {
height: 40px;
}
}
.login-center {
display: flex; display: flex;
flex: 1; position: absolute;
justify-content: space-around; top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90%;
max-width: 1200px;
z-index: 1;
align-items: center; align-items: center;
.desc-content { justify-content: center;
color: #fff; min-height: 600px;
//
.left-panel {
flex: 1;
height: 100%;
min-height: 700px;
background: rgba(4, 121, 231, 0.463); //
display: flex; display: flex;
flex-direction: column; align-items: center;
margin-top: -150x; justify-content: center;
width: 600px; // backdrop-filter: blur(1px);
.desc-title { padding: 60px 50px;
.title-text {
width: 80%;
font-size: 28px;
font-weight: 700; font-weight: 700;
font-style: normal;
font-size: 38px;
color: #ffffff;
margin-bottom: 56px;
}
.desc-remark {
line-height: 40px;
font-weight: 400;
font-style: normal;
font-size: 22px;
color: #ffffff; color: #ffffff;
position: absolute;
top: 5%;
left: 5%;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
} }
} }
.login-form {
box-sizing: border-box; //
width: 462px; .right-panel {
padding: 50px 30px; flex: 1;
background: #fff; height: 100%;
font-size: 17px; background: #ffffff;
display: flex;
align-items: center;
justify-content: center;
padding: 60px 50px;
.right-panel-content {
width: 83%;
min-height: 580px;
}
.login-title { .login-title {
font-size: 32px; font-size: 30px;
font-weight: 600;
color: #1890ff;
text-align: center; text-align: center;
color: #333; margin-bottom: 40px;
margin-bottom: 50px; text-align: left;
line-height: 130px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
} }
.operate-region {
display: flex; .login-form {
align-items: center;
.remember {
flex: 1;
text-align: left;
}
}
.login-btn {
margin-top: 20px;
height: 60px;
width: 100%; width: 100%;
} max-width: 400px;
.help-info {
max-width: 130px; //
overflow: hidden; .user-type-selector {
text-overflow: ellipsis; display: flex;
white-space: nowrap; margin-bottom: 30px;
} background: #f5f5f5;
.login-form-item { border-radius: 8px;
::v-deep(.ant-input-affix-wrapper) { padding: 4px;
height: 60px; gap: 4px;
line-height: 60px;
} .type-item {
::v-deep(.ant-input-prefix) { flex: 1;
padding-left: 50px; text-align: center;
} padding: 12px 20px;
::v-deep { border-radius: 6px;
.a-input__icon { cursor: pointer;
height: 100%; transition: all 0.3s;
font-size: 16px;
color: #666;
background: transparent;
&:hover {
background: rgba(24, 144, 255, 0.1);
}
&.active {
background: #595959;
color: #ffffff;
font-weight: 500;
}
} }
.ant-input-prefix { }
font-size: 20px;
padding: 0 10px; .login-form-item {
margin-bottom: 24px;
::v-deep(.el-form-item__label) {
padding-bottom: 8px;
font-size: 14px;
color: #333;
font-weight: 500;
} }
::v-deep(.el-input__wrapper) {
min-height: 48px;
border-radius: 4px;
.el-input__inner {
height: 48px;
line-height: 48px;
font-size: 14px;
}
&:hover {
box-shadow: 0 0 0 1px #40a9ff inset;
}
&.is-focus {
box-shadow: 0 0 0 1px #1890ff inset;
}
}
}
.form-options {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
.remember-password {
::v-deep(.el-checkbox) {
font-size: 14px;
color: #666;
.el-checkbox__label {
font-size: 14px;
color: #666;
}
}
}
.help-link {
color: #1890ff;
font-size: 14px;
text-decoration: none;
cursor: pointer;
&:hover {
color: #40a9ff;
text-decoration: underline;
}
}
}
.login-btn {
width: 100%;
height: 48px;
font-size: 16px;
font-weight: 500;
border-radius: 4px;
} }
} }
} }
} }
.copyright-info { .copyright-info {
width: 100%;
padding: 10px 0; padding: 10px 0;
text-align: center; text-align: center;
background: rgba(10, 37, 68, 0.6); background: rgba(10, 37, 68, 0.6);
color: #bbbbbb; color: #bbbbbb;
position: fixed;
z-index: 1;
font-size: 12px;
bottom: 0;
} }
} }
</style> </style>

View File

@ -0,0 +1,237 @@
<template>
<div class="login-container" :style="{ backgroundImage: `url(${configs.loginBg || '/web-common-resource/img/bg_login.png'})` }">
<div class="login-logo">
<img :src="configs.loginLogo" alt />
</div>
<div class="login-center">
<div class="desc-content">
<span class="desc-title">{{ configs.promotionalTitle }}</span>
<span class="desc-remark">{{ configs.promotionalContent }}</span>
</div>
<a-form :model="loginForm" ref="loginFormRef" label-position="left" label-width="0px" class="card-box login-form" @keyup.enter="handleLogin">
<div class="login-title">账号登录</div>
<a-form-item class="login-form-item" name="account" :rules="[{ ...required, message: '请输入用户名' }]">
<a-input v-model:value="loginForm.account" autocomplete="off" placeholder="登录账户">
<template #prefix>
<UserOutlined />
</template>
</a-input>
</a-form-item>
<a-tooltip v-model="capsTooltip" content="大写锁定已打开" placement="right" manual>
<a-form-item class="login-form-item" name="password" :rules="[{ ...required, message: '请输入密码' }]">
<a-input-password name="password" prefix-icon="el-icon-lock" v-model:value="loginForm.password" placeholder="密码" autocomplete="false" @blur="capsTooltip = false" @keyup="checkCapslock">
<template #prefix>
<LockOutlined />
</template>
</a-input-password>
</a-form-item>
</a-tooltip>
<a-form-item class="login-form-item">
<div class="operate-region">
<span class="remember">
<a-switch v-model:checked="remember"></a-switch>
<span class="m-l-xs">记住密码</span>
</span>
<a :href="`mailto:${configs.helpInformationLink}`" type="text" class="text-info pull-right help-info" :title="configs.helpInformationContent">{{ configs.helpInformationContent }}</a>
</div>
</a-form-item>
<a-button class="login-btn" type="primary" size="medium" :loading="loading" @click="handleLogin"></a-button>
</a-form>
</div>
<div class="copyright-info">{{ configs.copyrightInformation }}</div>
</div>
</template>
<script>
import { decrypt, encrypt } from 'utils/crypto'
import { login } from 'services'
import { reactive, toRefs, ref, computed } from 'vue'
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
import { useStore } from 'vuex'
import { useRouter, useRoute } from 'vue-router'
import setLoginData from './tools'
import { required } from '@/validate'
export default {
components: { UserOutlined, LockOutlined },
setup() {
const state = reactive({
remember: false,
loginForm: {
account: '',
password: ''
},
loading: false,
capsTooltip: false
})
const store = useStore()
const configs = computed(() => store.getters.pageConfig)
const loginFormRef = ref(null)
const init = () => {
const local = localStorage.getItem('cmcLoginData')
if (local) {
const obj = JSON.parse(local)
state.loginForm.account = obj.account
state.loginForm.password = decrypt(obj.password)
state.remember = true
}
}
init()
const router = useRouter()
const route = useRoute()
function goLogin(data) {
setLoginData(data)
if (state.remember) {
const obj = {
account: state.loginForm.account,
password: encrypt(state.loginForm.password)
}
localStorage.setItem('cmcLoginData', JSON.stringify(obj))
} else {
localStorage.removeItem('cmcLoginData')
}
const { redirect } = route.query
const path = redirect ? redirect.split('/#')[1] : '/sms-web/resource_dashboard'
router.replace(path)
localStorage.removeItem('lockData')
}
async function handleLogin() {
try {
state.loading = true
const values = await loginFormRef.value.validate()
const { account, password } = values
const res = await login({
account,
password: encrypt(password),
isManager: true
})
if (res.success) {
goLogin(res.data)
}
} catch (error) {}
state.loading = false
}
function checkCapslock({ shiftKey, key } = {}) {
if (key && key.length === 1) {
if ((shiftKey && key >= 'a' && key <= 'z') || (!shiftKey && key >= 'A' && key <= 'Z')) {
state.capsTooltip = true
} else {
state.capsTooltip = false
}
}
if (key === 'CapsLock' && this.capsTooltip === true) {
state.capsTooltip = false
}
}
return {
...toRefs(state),
required,
configs,
loginFormRef,
handleLogin,
checkCapslock
}
}
}
</script>
<style lang="scss" scoped>
.login-container {
display: flex;
flex-direction: column;
height: 100vh;
background: url('/web-common-resource/img/bg_login.png') #2d3a4b no-repeat;
background-size: cover;
.login-logo {
padding: 5px 40px;
img {
height: 40px;
}
}
.login-center {
display: flex;
flex: 1;
justify-content: space-around;
align-items: center;
.desc-content {
color: #fff;
display: flex;
flex-direction: column;
margin-top: -150x;
width: 600px;
.desc-title {
font-weight: 700;
font-style: normal;
font-size: 38px;
color: #ffffff;
margin-bottom: 56px;
}
.desc-remark {
line-height: 40px;
font-weight: 400;
font-style: normal;
font-size: 22px;
color: #ffffff;
}
}
.login-form {
box-sizing: border-box;
width: 462px;
padding: 50px 30px;
background: #fff;
font-size: 17px;
.login-title {
font-size: 32px;
text-align: center;
color: #333;
margin-bottom: 50px;
}
.operate-region {
display: flex;
align-items: center;
.remember {
flex: 1;
text-align: left;
}
}
.login-btn {
margin-top: 20px;
height: 60px;
width: 100%;
}
.help-info {
max-width: 130px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.login-form-item {
::v-deep(.ant-input-affix-wrapper) {
height: 60px;
line-height: 60px;
}
::v-deep(.ant-input-prefix) {
padding-left: 50px;
}
::v-deep {
.a-input__icon {
height: 100%;
}
.ant-input-prefix {
font-size: 20px;
padding: 0 10px;
}
}
}
}
}
.copyright-info {
padding: 10px 0;
text-align: center;
background: rgba(10, 37, 68, 0.6);
color: #bbbbbb;
}
}
</style>