初始化
This commit is contained in:
386
addons/webman/views/activity_tabs.vue
Normal file
386
addons/webman/views/activity_tabs.vue
Normal file
@@ -0,0 +1,386 @@
|
||||
<template>
|
||||
<a-form layout="vertical" ref="formRef" @finish="onFinish" @finishFailed="onFinishFailed" :model="activity"
|
||||
:label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<a-tabs v-model:activeKey="activeKey">
|
||||
<a-tab-pane key="activeKey_content" :tab="activity_content">
|
||||
<a-form-item :label="activity_type" name="type">
|
||||
<a-radio-group v-model:value="activity.type">
|
||||
<a-radio-button value="1">{{ activity_cycle }}</a-radio-button>
|
||||
<a-radio-button value="2">{{ activity_custom }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="activity.type === '2'" v-model:label="RangePicker.showTime" name="range_time"
|
||||
v-bind="rangeConfig">
|
||||
<a-range-picker v-model:value="activity.range_time" format="YYYY-MM-DD HH:mm:ss" show-time
|
||||
value-format="YYYY-MM-DD HH:mm:ss"/>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="activity.type === '1'" :label="cycle_type"
|
||||
:rules="[{ required: true, message: cycle_type_required }]"
|
||||
name="cycle_type">
|
||||
<a-select v-model:value="activity.cycle_type" :placeholder="placeholder_cycle_type"
|
||||
style="width: 200px"
|
||||
@change="handleProvinceChange">
|
||||
<a-select-option v-for="(cycle_type, index) in cycleTypes" :key="cycle_type" :value="cycle_type">
|
||||
{{ cycle_type }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="activity.type === '1'" :label="cycle_data"
|
||||
:rules="[{ required: true, message: cycle_data_required }]"
|
||||
name="cycle_data">
|
||||
<a-select v-model:value="activity.cycle_data" :placeholder="placeholder_cycle_data"
|
||||
style="width: 200px">
|
||||
<a-select-option v-for="(cycle_data, index) in cycleDatas" :key="cycle_data" :value="index">
|
||||
{{ cycle_data }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item hidden name="id">
|
||||
<a-input v-model:value="activity.id" name="id" type="hidden"/>
|
||||
</a-form-item>
|
||||
<a-tabs v-model:activeKey="activeKey_lang_content">
|
||||
<a-tab-pane v-for="(lang, lang_index) in langs" :key="lang_index" :tab="lang.value" forceRender="true">
|
||||
<a-form-item :label="activity_picture" :name="['activity_content', lang.key, 'picture']"
|
||||
:rules="[{ required: true, message: picture_required }]">
|
||||
<a-upload v-model:file-list="activity.activity_content[lang.key].picture"
|
||||
:before-upload="beforeUpload" :headers="headers"
|
||||
:max-count="1" action="/ex-admin/addons-webman-controller-IndexController/activityUpload"
|
||||
list-type="picture-card">
|
||||
<div>
|
||||
<PlusOutlined/>
|
||||
<div style="margin-top: 8px">Upload</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
</a-form-item>
|
||||
<a-form-item :name="['activity_content', lang.key, 'id']" hidden>
|
||||
<a-input v-model:value="activity.activity_content[lang.key].id" type="hidden"/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="activity_name" :name="['activity_content', lang.key, 'name']"
|
||||
:rules="[{ required: true, message: name_required }]">
|
||||
<a-input v-model:value="activity.activity_content[lang.key].name" :maxlength="100"
|
||||
:rules="[{ required: true, message: activity_name_required }]"
|
||||
show-count/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="recharge_setting" :rules="[{ required: true, message: recharge_setting_required }]"
|
||||
name="recharge_id">
|
||||
<a-select v-model:value="activity.recharge_id" allowClear="true">
|
||||
<a-select-option v-for="option in options" :key="option.id" :value="option.id">
|
||||
{{ option.title }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<a-form-item>
|
||||
<a-button type="primary" html-type="submit">{{ Submit }}</a-button>
|
||||
<a-button style="margin-left: 10px" @click="resetForm">{{ reset }}</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
<script>
|
||||
const cycleType = ['Week', 'Month'];
|
||||
const cycleData = {
|
||||
Week: ['星期天', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
|
||||
Month: ['1', '2', '3', '4', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31'],
|
||||
};
|
||||
|
||||
const messages = {
|
||||
//简体中文
|
||||
'zh-CN': {
|
||||
rangTime: '请选择开放时间',
|
||||
showTime: '开放时间',
|
||||
submit: '提交',
|
||||
reset: '重置',
|
||||
activity_content: '活动内容',
|
||||
activity_picture: '活动图片',
|
||||
activity_type: '活动时间模式',
|
||||
activity_cycle: '周期模式',
|
||||
activity_custom: '自定义模式',
|
||||
activity_cycle_data: '周期数据',
|
||||
activity_picture_required: '请上传活动图片',
|
||||
activity_name: '活动名称',
|
||||
activity_name_required: '请填写活动名称',
|
||||
activity_link: '活动链接',
|
||||
upload_type: '支持png, jpeg, png图片格式',
|
||||
picture_required: '请上传活动图片',
|
||||
name_required: '请填写活动名称',
|
||||
recharge_setting: '充值配置',
|
||||
recharge_setting_required: '请选择充值配置',
|
||||
placeholder_cycle_type: '请选择周期模式',
|
||||
placeholder_cycle_data: '请选择周期配置数据',
|
||||
cycle_type_help: '周期模式下,周期类型以及周期配置为必选项',
|
||||
cycle_type: '周期类型',
|
||||
cycle_data: '周期配置',
|
||||
cycle_type_required: '请选择周期类型',
|
||||
cycle_data_required: '请选择周期配置',
|
||||
},
|
||||
//英文
|
||||
en: {
|
||||
rangTime: '请选择开放时间',
|
||||
showTime: '开放时间',
|
||||
submit: '提交',
|
||||
reset: '重置',
|
||||
activity_content: '活动内容',
|
||||
activity_picture: '活动图片',
|
||||
activity_type: '活动时间模式',
|
||||
activity_cycle: '周期模式',
|
||||
activity_custom: '自定义模式',
|
||||
activity_cycle_data: '周期数据',
|
||||
activity_picture_required: '请上传活动图片',
|
||||
activity_name: '活动名称',
|
||||
activity_name_required: '请填写活动名称',
|
||||
activity_link: '活动链接',
|
||||
upload_type: '支持png, jpeg, png图片格式',
|
||||
upload_size: '图片最大不得超过5M',
|
||||
picture_required: '请上传活动图片',
|
||||
name_required: '请填写活动名称',
|
||||
recharge_setting: '充值配置',
|
||||
recharge_setting_required: '请选择充值配置',
|
||||
placeholder_cycle_type: '请选择周期模式',
|
||||
placeholder_cycle_data: '请选择周期配置数据',
|
||||
cycle_type_help: '周期模式下,模式类型以及相关配置为必选项',
|
||||
cycle_type: '周期类型',
|
||||
cycle_data: '周期配置',
|
||||
cycle_type_required: '请选择周期类型',
|
||||
cycle_data_required: '请选择周期配置',
|
||||
},
|
||||
'Ma-my': {
|
||||
rangTime: '请选择开放时间',
|
||||
showTime: '开放时间',
|
||||
submit: '提交',
|
||||
reset: '重置',
|
||||
activity_content: '活动内容',
|
||||
activity_picture: '活动图片',
|
||||
activity_type: '活动时间模式',
|
||||
activity_cycle: '周期模式',
|
||||
activity_custom: '自定义模式',
|
||||
activity_cycle_data: '周期数据',
|
||||
activity_picture_required: '请上传活动图片',
|
||||
activity_name: '活动名称',
|
||||
activity_name_required: '请填写活动名称',
|
||||
activity_link: '活动链接',
|
||||
upload_type: '支持png, jpeg, png图片格式',
|
||||
upload_size: '图片最大不得超过5M',
|
||||
picture_required: '请上传活动图片',
|
||||
name_required: '请填写活动名称',
|
||||
recharge_setting: '充值配置',
|
||||
recharge_setting_required: '请选择充值配置',
|
||||
placeholder_cycle_type: '请选择周期模式',
|
||||
placeholder_cycle_data: '请选择周期配置数据',
|
||||
cycle_type_help: '周期模式下,模式类型以及相关配置为必选项',
|
||||
cycle_type: '周期类型',
|
||||
cycle_data: '周期配置',
|
||||
cycle_type_required: '请选择周期类型',
|
||||
cycle_data_required: '请选择周期配置',
|
||||
},
|
||||
// 繁体中文
|
||||
'cam_dia': {
|
||||
rangTime: '请选择开放时间',
|
||||
showTime: '开放时间',
|
||||
submit: '提交',
|
||||
reset: '重置',
|
||||
activity_content: '活动内容',
|
||||
activity_picture: '活动图片',
|
||||
activity_type: '活动时间模式',
|
||||
activity_cycle: '周期模式',
|
||||
activity_custom: '自定义模式',
|
||||
activity_cycle_data: '周期数据',
|
||||
activity_picture_required: '请上传活动图片',
|
||||
activity_name: '活动名称',
|
||||
activity_name_required: '请填写活动名称',
|
||||
activity_link: '活动链接',
|
||||
upload_type: '支持png, jpeg, png图片格式',
|
||||
upload_size: '图片最大不得超过5M',
|
||||
picture_required: '请上传活动图片',
|
||||
name_required: '请填写活动名称',
|
||||
recharge_setting: '充值配置',
|
||||
recharge_setting_required: '请选择充值配置',
|
||||
placeholder_cycle_type: '请选择周期模式',
|
||||
placeholder_cycle_data: '请选择周期配置数据',
|
||||
cycle_type_help: '周期模式下,模式类型以及相关配置为必选项',
|
||||
cycle_type: '周期类型',
|
||||
cycle_data: '周期配置',
|
||||
cycle_type_required: '请选择周期类型',
|
||||
cycle_data_required: '请选择周期配置',
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: "socket.vue",
|
||||
//可传参数
|
||||
props: {
|
||||
activityModel: {},
|
||||
rechargeSetting: [],
|
||||
langs: {},
|
||||
showTime: String,
|
||||
langLocale: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cycleTypes: cycleType,
|
||||
cycleDatas: cycleData[cycleType[1]],
|
||||
activity: this.activityModel,
|
||||
options: this.rechargeSetting,
|
||||
activeKey: 'activeKey_content',
|
||||
newTabIndex: '',
|
||||
activeKey_lang_content: 0,
|
||||
activeKey_lang_phase: 0,
|
||||
rangeConfig: {
|
||||
rules: [{
|
||||
type: 'array',
|
||||
required: true,
|
||||
message: messages[this.langLocale]['rangTime'],
|
||||
}],
|
||||
},
|
||||
RangePicker: Vue.reactive({
|
||||
showTime: messages[this.langLocale]['showTime']
|
||||
}),
|
||||
labelCol: {
|
||||
style: {
|
||||
width: '150px',
|
||||
},
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 24,
|
||||
},
|
||||
headers: {
|
||||
authorization: localStorage.getItem("agent_ex-admin-token"),
|
||||
},
|
||||
Submit: messages[this.langLocale]['submit'],
|
||||
reset: messages[this.langLocale]['reset'],
|
||||
activity_content: messages[this.langLocale]['activity_content'],
|
||||
activity_picture: messages[this.langLocale]['activity_picture'],
|
||||
activity_type: messages[this.langLocale]['activity_type'],
|
||||
activity_custom: messages[this.langLocale]['activity_custom'],
|
||||
activity_cycle: messages[this.langLocale]['activity_cycle'],
|
||||
activity_cycle_data: messages[this.langLocale]['activity_cycle_data'],
|
||||
activity_name: messages[this.langLocale]['activity_name'],
|
||||
activity_name_required: messages[this.langLocale]['activity_name_required'],
|
||||
activity_link: messages[this.langLocale]['activity_link'],
|
||||
activity_notice: messages[this.langLocale]['activity_notice'],
|
||||
upload_type: messages[this.langLocale]['upload_type'],
|
||||
upload_size: messages[this.langLocale]['upload_size'],
|
||||
picture_required: messages[this.langLocale]['picture_required'],
|
||||
name_required: messages[this.langLocale]['name_required'],
|
||||
recharge_setting: messages[this.langLocale]['recharge_setting'],
|
||||
recharge_setting_required: messages[this.langLocale]['recharge_setting_required'],
|
||||
placeholder_cycle_type: messages[this.langLocale]['placeholder_cycle_type'],
|
||||
placeholder_cycle_data: messages[this.langLocale]['placeholder_cycle_data'],
|
||||
cycle_type_help: messages[this.langLocale]['cycle_type_help'],
|
||||
cycle_type: messages[this.langLocale]['cycle_type'],
|
||||
cycle_data: messages[this.langLocale]['cycle_data'],
|
||||
cycle_type_required: messages[this.langLocale]['cycle_type_required'],
|
||||
cycle_data_required: messages[this.langLocale]['cycle_data_required'],
|
||||
selectedItems: Vue.ref([]),
|
||||
};
|
||||
},
|
||||
//生命周期渲染完执行
|
||||
created() {
|
||||
this.newTabIndex = Vue.ref(0);
|
||||
if (this.activity.id === null || this.activity.id === undefined || this.activity.id === '') {
|
||||
this.activity = Vue.reactive({
|
||||
id: '',
|
||||
type: '2',
|
||||
cycle_type: null,
|
||||
cycle_data: null,
|
||||
range_time: {
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
},
|
||||
dateRange: ['', ''],
|
||||
activity_content: {
|
||||
'zh-CN': {
|
||||
name: '',
|
||||
lang: 'zh-CN',
|
||||
picture: [],
|
||||
id: '',
|
||||
},
|
||||
en: {
|
||||
name: '',
|
||||
lang: 'en',
|
||||
picture: [],
|
||||
id: '',
|
||||
},
|
||||
'Ma-my': {
|
||||
name: '',
|
||||
lang: 'Ma-my',
|
||||
picture: [],
|
||||
id: '',
|
||||
},
|
||||
'cam_dia': {
|
||||
name: '',
|
||||
lang: 'cam_dia',
|
||||
picture: [],
|
||||
id: '',
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if (this.activity.type === '1') {
|
||||
this.cycleDatas = cycleData[this.activity.cycle_type];
|
||||
} else {
|
||||
this.cycleTypes = cycleType;
|
||||
this.cycleDatas = cycleData['Week'];
|
||||
this.activity.cycle_type = null;
|
||||
this.activity.cycle_data = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
//定义函数方法
|
||||
methods: {
|
||||
beforeUpload(file) {
|
||||
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg';
|
||||
if (!isJpgOrPng) {
|
||||
this.$message.error(this.upload_type);
|
||||
}
|
||||
const isLt2M = file.size / 1024 / 1024 < 5;
|
||||
if (!isLt2M) {
|
||||
this.$message.error(this.upload_size);
|
||||
}
|
||||
return isJpgOrPng && isLt2M;
|
||||
},
|
||||
resetForm() {
|
||||
this.$refs.formRef.resetFields();
|
||||
},
|
||||
onFinish(values) {
|
||||
this.$request({
|
||||
url: '/ex-admin/addons-webman-controller-ActivityController/activityOperate',
|
||||
method: 'post',
|
||||
data: values,
|
||||
header: this.headers
|
||||
}).then(res => {
|
||||
if (res.code === 200) {
|
||||
// location.reload();
|
||||
}
|
||||
})
|
||||
},
|
||||
onFinishFailed(errorInfo) {
|
||||
let name = errorInfo.errorFields[0]['name'];
|
||||
if (name.length > 0) {
|
||||
switch (name[0]) {
|
||||
case 'range_time':
|
||||
this.activeKey = 'activeKey_content';
|
||||
break;
|
||||
case 'activity_content':
|
||||
this.activeKey = 'activeKey_content';
|
||||
if (name[1] !== 'undefined' && name[1] != null && name[1] !== '') {
|
||||
let ac = 0;
|
||||
for (let key in this.activity.activity_content) {
|
||||
if (key === name[1]) {
|
||||
this.activeKey_lang_content = ac;
|
||||
}
|
||||
ac++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
handleProvinceChange(value) {
|
||||
this.cycleDatas = cycleData[value];
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
322
addons/webman/views/agent.vue
Normal file
322
addons/webman/views/agent.vue
Normal file
@@ -0,0 +1,322 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="login-layout">
|
||||
<div class="left">
|
||||
<div class="logo-container">
|
||||
<img v-if="webLogo" class="logo" src="/exadmin/img/login_logo.png"/>
|
||||
</div>
|
||||
<div class="left-container">
|
||||
<img class="ad" src="/exadmin/img/login-box-bg.9027741f.svg">
|
||||
<div class="text-block">
|
||||
{{ webName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="login-container">
|
||||
<a-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
|
||||
<div class="title-container">
|
||||
<h3 class="title">
|
||||
<span>{{ agent_login }}</span>
|
||||
</h3>
|
||||
</div>
|
||||
<a-form-item name="username">
|
||||
<a-input
|
||||
v-model:value="loginForm.username"
|
||||
auto-complete="on"
|
||||
placeholder:enter_account
|
||||
size="large"
|
||||
tabindex="1"
|
||||
>
|
||||
<template #prefix>
|
||||
<UserOutlined/>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item name="password">
|
||||
<a-input-password
|
||||
v-model:value="loginForm.password"
|
||||
auto-complete="on"
|
||||
placeholder:enter_password
|
||||
size="large"
|
||||
tabindex="2"
|
||||
@keyup.enter.native="handleLogin"
|
||||
>
|
||||
<template #prefix>
|
||||
<LockOutlined/>
|
||||
</template>
|
||||
</a-input-password>
|
||||
</a-form-item>
|
||||
<div v-if="verification" style="display: flex;justify-content: space-between;">
|
||||
<a-form-item name="verify" style="flex:1;margin-right: 10px">
|
||||
<a-input
|
||||
v-model:value="loginForm.verify"
|
||||
auto-complete="on"
|
||||
maxlength="4"
|
||||
placeholder:enter_verify
|
||||
size="large"
|
||||
tabindex="3"
|
||||
@keyup.enter.native="handleLogin"
|
||||
>
|
||||
<template #prefix>
|
||||
<SafetyCertificateOutlined/>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<img :height="40" :src="verifyImage" class="verify" @click="getVerify"/>
|
||||
</div>
|
||||
<a-button :loading="loading" block size="large" type="primary" @click="handleLogin">{{ loginBtnText }}
|
||||
</a-button>
|
||||
</a-form>
|
||||
</div>
|
||||
<div class="icp"><a href="http://beian.miit.gov.cn" target="_blank">{{ webMiitbeian }}</a> | {{ webCopyright }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Agent',
|
||||
props: {
|
||||
webLogo: String,
|
||||
webName: String,
|
||||
webCopyright: String,
|
||||
webMiitbeian: String,
|
||||
deBug: Boolean,
|
||||
agent_login: String,
|
||||
admin_login: String,
|
||||
enter_account: String,
|
||||
enter_password: String,
|
||||
enter_verify: String,
|
||||
password_verify: String,
|
||||
login: String,
|
||||
},
|
||||
data() {
|
||||
const validatePassword = (rule, value, callback) => {
|
||||
if (value.length < 5) {
|
||||
return Promise.reject(this.password_verify)
|
||||
} else {
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
return {
|
||||
verification: false,
|
||||
loginForm: {
|
||||
username: '',
|
||||
password: '',
|
||||
verify: '',
|
||||
hash: '',
|
||||
source: 'agent',
|
||||
},
|
||||
loginRules: {
|
||||
username: [{required: true, trigger: 'change', message: this.enter_account}],
|
||||
verify: [{required: true, message: this.enter_verify}],
|
||||
password: [{required: true, trigger: 'change', validator: validatePassword}]
|
||||
},
|
||||
loading: false,
|
||||
verifyImage: '',
|
||||
loginBtnText: '登录',
|
||||
redirect: null,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route: {
|
||||
handler: function (route) {
|
||||
if (route.query && route.query.redirect) {
|
||||
const index = route.fullPath.indexOf('?redirect=')
|
||||
if (index > -1) {
|
||||
this.redirect = route.fullPath.substr(index + 10)
|
||||
}
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.deBug) {
|
||||
this.loginForm.username = '';
|
||||
this.loginForm.password = '';
|
||||
}
|
||||
this.getVerify()
|
||||
},
|
||||
methods: {
|
||||
getVerify() {
|
||||
this.$request({
|
||||
url: 'ex-admin/login/captcha'
|
||||
}).then(res => {
|
||||
this.verifyImage = res.data.image
|
||||
this.loginForm.hash = res.data.hash
|
||||
this.verification = res.data.verification
|
||||
})
|
||||
},
|
||||
|
||||
handleLogin(data) {
|
||||
this.$refs.loginForm.validate().then(() => {
|
||||
this.loading = true
|
||||
this.$action.login(this.loginForm).then(res => {
|
||||
this.$router.push(this.redirect || '/')
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.getVerify()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
.logo {
|
||||
|
||||
}
|
||||
|
||||
.login-layout .left {
|
||||
position: relative;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
margin-left: 150px;
|
||||
}
|
||||
|
||||
.login-layout .left .ad {
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
.login-layout .right {
|
||||
position: relative;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.icp {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
|
||||
width: 100%;
|
||||
color: #000;
|
||||
opacity: .5;
|
||||
font-size: 12px;
|
||||
|
||||
}
|
||||
|
||||
.icp a {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@keyframes bg-run {
|
||||
0% {
|
||||
background-position-x: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
background-position-x: -1920px;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.container:before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-left: -48%;
|
||||
background-image: url("/exadmin/img/login-bg.b9f5c736.svg");
|
||||
background-position: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: auto 100%;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 30px;
|
||||
font-size: 32px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
position: relative;
|
||||
top: 50px;
|
||||
margin-left: 20px;
|
||||
|
||||
}
|
||||
|
||||
.logo-container img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.login-layout {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.left-container {
|
||||
position: absolute;
|
||||
top: calc(50% - 100px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
width: 400px;
|
||||
position: absolute;
|
||||
top: calc(50% - 250px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.login-container .login-form {
|
||||
|
||||
}
|
||||
|
||||
.login-container .tips {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.login-container .svg-container {
|
||||
padding: 6px 5px 6px 15px;
|
||||
color: #889aa4;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.login-container .title-container .title {
|
||||
font-size: 26px;
|
||||
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.login-container .show-pwd {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 7px;
|
||||
font-size: 16px;
|
||||
color: #889aa4;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.verify {
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
</style>
|
||||
322
addons/webman/views/login.vue
Normal file
322
addons/webman/views/login.vue
Normal file
@@ -0,0 +1,322 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="login-layout">
|
||||
<div class="left">
|
||||
<div class="logo-container">
|
||||
<img v-if="webLogo" class="logo" src="/exadmin/img/login_logo.png"/>
|
||||
</div>
|
||||
<div class="left-container">
|
||||
<img class="ad" src="/exadmin/img/login-box-bg.9027741f.svg">
|
||||
<div class="text-block">
|
||||
{{ webName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="login-container">
|
||||
<a-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
|
||||
<div class="title-container">
|
||||
<h3 class="title">
|
||||
<span>{{ admin_login }}</span>
|
||||
</h3>
|
||||
</div>
|
||||
<a-form-item name="username">
|
||||
<a-input
|
||||
v-model:value="loginForm.username"
|
||||
auto-complete="on"
|
||||
placeholder:enter_account
|
||||
size="large"
|
||||
tabindex="1"
|
||||
>
|
||||
<template #prefix>
|
||||
<UserOutlined/>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item name="password">
|
||||
<a-input-password
|
||||
v-model:value="loginForm.password"
|
||||
auto-complete="on"
|
||||
placeholder:enter_password
|
||||
size="large"
|
||||
tabindex="2"
|
||||
@keyup.enter.native="handleLogin"
|
||||
>
|
||||
<template #prefix>
|
||||
<LockOutlined/>
|
||||
</template>
|
||||
</a-input-password>
|
||||
</a-form-item>
|
||||
<div v-if="verification" style="display: flex;justify-content: space-between;">
|
||||
<a-form-item name="verify" style="flex:1;margin-right: 10px">
|
||||
<a-input
|
||||
v-model:value="loginForm.verify"
|
||||
auto-complete="on"
|
||||
maxlength="4"
|
||||
placeholder:enter_verify
|
||||
size="large"
|
||||
tabindex="3"
|
||||
@keyup.enter.native="handleLogin"
|
||||
>
|
||||
<template #prefix>
|
||||
<SafetyCertificateOutlined/>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<img :height="40" :src="verifyImage" class="verify" @click="getVerify"/>
|
||||
</div>
|
||||
<a-button :loading="loading" block size="large" type="primary" @click="handleLogin">{{ loginBtnText }}
|
||||
</a-button>
|
||||
</a-form>
|
||||
</div>
|
||||
<div class="icp"><a href="http://beian.miit.gov.cn" target="_blank">{{ webMiitbeian }}</a> | {{ webCopyright }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Login',
|
||||
props: {
|
||||
webLogo: String,
|
||||
webName: String,
|
||||
webCopyright: String,
|
||||
webMiitbeian: String,
|
||||
deBug: Boolean,
|
||||
agent_login: String,
|
||||
admin_login: String,
|
||||
enter_account: String,
|
||||
enter_password: String,
|
||||
enter_verify: String,
|
||||
password_verify: String,
|
||||
login: String,
|
||||
},
|
||||
data() {
|
||||
const validatePassword = (rule, value, callback) => {
|
||||
if (value.length < 5) {
|
||||
return Promise.reject(this.password_verify)
|
||||
} else {
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
return {
|
||||
verification: false,
|
||||
loginForm: {
|
||||
username: '',
|
||||
password: '',
|
||||
verify: '',
|
||||
hash: '',
|
||||
source: 'admin',
|
||||
},
|
||||
loginRules: {
|
||||
username: [{required: true, trigger: 'change', message: this.enter_account}],
|
||||
verify: [{required: true, message: this.enter_verify}],
|
||||
password: [{required: true, trigger: 'change', validator: validatePassword}]
|
||||
},
|
||||
loading: false,
|
||||
verifyImage: '',
|
||||
loginBtnText: this.login,
|
||||
redirect: null,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route: {
|
||||
handler: function (route) {
|
||||
if (route.query && route.query.redirect) {
|
||||
const index = route.fullPath.indexOf('?redirect=')
|
||||
if (index > -1) {
|
||||
this.redirect = route.fullPath.substr(index + 10)
|
||||
}
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.deBug) {
|
||||
this.loginForm.username = '';
|
||||
this.loginForm.password = '';
|
||||
}
|
||||
this.getVerify()
|
||||
},
|
||||
methods: {
|
||||
getVerify() {
|
||||
this.$request({
|
||||
url: 'ex-admin/login/captcha'
|
||||
}).then(res => {
|
||||
this.verifyImage = res.data.image
|
||||
this.loginForm.hash = res.data.hash
|
||||
this.verification = res.data.verification
|
||||
})
|
||||
},
|
||||
|
||||
handleLogin(data) {
|
||||
this.$refs.loginForm.validate().then(() => {
|
||||
this.loading = true
|
||||
this.$action.login(this.loginForm).then(res => {
|
||||
this.$router.push(this.redirect || '/')
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.getVerify()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
.logo {
|
||||
|
||||
}
|
||||
|
||||
.login-layout .left {
|
||||
position: relative;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
margin-left: 150px;
|
||||
}
|
||||
|
||||
.login-layout .left .ad {
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
.login-layout .right {
|
||||
position: relative;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.icp {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
|
||||
width: 100%;
|
||||
color: #000;
|
||||
opacity: .5;
|
||||
font-size: 12px;
|
||||
|
||||
}
|
||||
|
||||
.icp a {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@keyframes bg-run {
|
||||
0% {
|
||||
background-position-x: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
background-position-x: -1920px;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.container:before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-left: -48%;
|
||||
background-image: url("/exadmin/img/login-bg.b9f5c736.svg");
|
||||
background-position: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: auto 100%;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.text-block {
|
||||
margin-top: 30px;
|
||||
font-size: 32px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
position: relative;
|
||||
top: 50px;
|
||||
margin-left: 20px;
|
||||
|
||||
}
|
||||
|
||||
.logo-container img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.login-layout {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.left-container {
|
||||
position: absolute;
|
||||
top: calc(50% - 100px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
width: 400px;
|
||||
position: absolute;
|
||||
top: calc(50% - 250px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.login-container .login-form {
|
||||
|
||||
}
|
||||
|
||||
.login-container .tips {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.login-container .svg-container {
|
||||
padding: 6px 5px 6px 15px;
|
||||
color: #889aa4;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.login-container .title-container .title {
|
||||
font-size: 26px;
|
||||
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.login-container .show-pwd {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 7px;
|
||||
font-size: 16px;
|
||||
color: #889aa4;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.verify {
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
</style>
|
||||
72
addons/webman/views/machine_status.vue
Normal file
72
addons/webman/views/machine_status.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-tag color="processing" v-model="machine_status" v-if="isOnline">
|
||||
<template #icon>
|
||||
<sync-outlined :spin="true"/>
|
||||
</template>
|
||||
在线
|
||||
</a-tag>
|
||||
<a-tag color="default" v-model="machine_status" v-else>
|
||||
<template #icon>
|
||||
<minus-circle-outlined/>
|
||||
</template>
|
||||
离线
|
||||
</a-tag>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "machine_status.vue",
|
||||
props: {
|
||||
id: String,
|
||||
type: String,
|
||||
department_id: String,
|
||||
ws: String,
|
||||
machine_status: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isOnline: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
if (this.machine_status === 'online') {
|
||||
this.isOnline = true;
|
||||
}
|
||||
if (this.ws) {
|
||||
this.$nextTick(() => {
|
||||
this.loadScript('/plugin/webman/push/push.js').then(() => {
|
||||
let connection = new Push({
|
||||
url: this.ws,
|
||||
app_key: '20f94408fc4c52845f162e92a253c7a3',
|
||||
auth: '/plugin/webman/push/auth'
|
||||
});
|
||||
let type = this.type;
|
||||
let machine_id = this.id;
|
||||
let department_id = this.department_id;
|
||||
let group_channel = connection.subscribe('private-admin_group-' + type + '-' + department_id + '-' + machine_id);
|
||||
group_channel.on('message', (data) => {
|
||||
let content = JSON.parse(data.content);
|
||||
switch (content.msg_type) {
|
||||
case 'machine_now_status':
|
||||
this.isOnline = content.machine_status === 'online';
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadScript(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement('script');
|
||||
script.src = url;
|
||||
script.onload = resolve;
|
||||
script.onerror = reject;
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
206
addons/webman/views/media_play.vue
Normal file
206
addons/webman/views/media_play.vue
Normal file
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<div class="media_btn">
|
||||
<a-button shape="circle" @click="showActionDrawer" size="small">
|
||||
<template #icon>
|
||||
<interaction-outlined/>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button shape="circle" @click="isActive = !isActive" size="small">
|
||||
<template #icon>
|
||||
<sync-outlined/>
|
||||
</template>
|
||||
</a-button>
|
||||
<a-button shape="circle" @click="showDrawer" size="small">
|
||||
<template #icon>
|
||||
<play-circle-outlined/>
|
||||
</template>
|
||||
</a-button>
|
||||
</div>
|
||||
<div :class="{ animate_left:is_move }">
|
||||
<a-spin :spinning="spinning" wrapperClassName="iframe_video">
|
||||
<iframe
|
||||
class="iframe_box"
|
||||
:src="iframe_src"
|
||||
@load="handleIframeLoad" sandbox='allow-scripts allow-same-origin allow-popups' ref="media" frameborder="0"
|
||||
allowfullscreen v-bind:class="{ active: isActive }" id="my-iframe"
|
||||
:key="refreshKey"></iframe>
|
||||
</a-spin>
|
||||
</div>
|
||||
<a-drawer
|
||||
:title="play_address"
|
||||
placement="right"
|
||||
:closable="false"
|
||||
:visible="action_visible"
|
||||
:get-container="false"
|
||||
:style="{ position: 'absolute' }"
|
||||
@close="onClose"
|
||||
width="44%"
|
||||
:maskStyle="{opacity:0}"
|
||||
>
|
||||
<a-button type="dashed" @click="handleMenuClick(item.key)" shape="round" size="small" v-for="(item) in action_list"
|
||||
:key="item.key" style="margin: 6px">
|
||||
<template #icon>
|
||||
<tool-outlined/>
|
||||
</template>
|
||||
{{ item.action }}
|
||||
</a-button>
|
||||
</a-drawer>
|
||||
<a-drawer
|
||||
:title="play_address"
|
||||
placement="right"
|
||||
:closable="false"
|
||||
:visible="visible"
|
||||
:get-container="false"
|
||||
:style="{ position: 'absolute' }"
|
||||
@close="onClose"
|
||||
width="44%"
|
||||
:maskStyle="{opacity:0}"
|
||||
>
|
||||
<a-list item-layout="horizontal" :data-source="iframe_list">
|
||||
<template #renderItem="{ item,index }">
|
||||
<a-list-item>
|
||||
<a-list-item-meta :description="item.desc">
|
||||
<template #title>
|
||||
<a href="javascript:void(0);" @click="changeMedia(item.src, index)"
|
||||
:class="index === typeSelected ?'active_media':''">{{ item.title }}</a>
|
||||
</template>
|
||||
<template #avatar>
|
||||
<video-camera-add-outlined/>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-drawer>
|
||||
<div>
|
||||
<a-modal v-model:visible="open_visible" title="自定义开分" @ok="openAnyPoint" destroyOnClose="true"
|
||||
maskClosable="true" width="300px">
|
||||
<a-input-number v-model:value="open_any_point_value" addon-before="+" addon-after="point" :max="5000" :min="0"
|
||||
:step="1" :precision="0"></a-input-number>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
iframe_src: String,
|
||||
btn_text: String,
|
||||
iframe_list: String,
|
||||
play_address: String,
|
||||
type: String,
|
||||
machine_id: String,
|
||||
action_list: []
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
spinning: true,
|
||||
isActive: true,
|
||||
refreshKey: 0,
|
||||
visible: false,
|
||||
typeSelected: 0,
|
||||
open_visible: false,
|
||||
open_any_point_value: null,
|
||||
open_any_point_cmd: '4A',
|
||||
is_move: false,
|
||||
action_visible: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleIframeLoad() {
|
||||
this.spinning = false;
|
||||
},
|
||||
showDrawer() {
|
||||
this.visible = true;
|
||||
this.is_move = true;
|
||||
},
|
||||
showActionDrawer() {
|
||||
this.action_visible = true;
|
||||
this.is_move = true;
|
||||
},
|
||||
onClose() {
|
||||
this.visible = false;
|
||||
this.action_visible = false;
|
||||
this.is_move = false;
|
||||
},
|
||||
handleMenuClick(cmd) {
|
||||
if (cmd === this.open_any_point_cmd) {
|
||||
this.open_visible = true;
|
||||
} else {
|
||||
this.sendCmd(cmd);
|
||||
}
|
||||
},
|
||||
sendCmd(cmd, data = null) {
|
||||
this.$request({
|
||||
url: 'ex-admin/system/doMachineCmd',
|
||||
method: 'post',
|
||||
data: {
|
||||
'cmd': cmd,
|
||||
'data': data,
|
||||
'machine_id': this.machine_id,
|
||||
},
|
||||
}).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message.success('操作成功');
|
||||
} else {
|
||||
this.$message.error(res.message ? res.message : res.msg);
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$message.error(error.message);
|
||||
})
|
||||
},
|
||||
openAnyPoint() {
|
||||
this.sendCmd(this.open_any_point_cmd, this.open_any_point_value)
|
||||
this.open_visible = false;
|
||||
},
|
||||
changeMedia(src, index) {
|
||||
this.$props.iframe_src = src;
|
||||
this.isActive = false;
|
||||
this.refreshKey++
|
||||
this.visible = false;
|
||||
this.open_visible = false;
|
||||
this.typeSelected = index;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.active {
|
||||
transform: rotate(270deg)
|
||||
}
|
||||
|
||||
.active_media {
|
||||
color: rgb(24, 144, 255) !important;
|
||||
}
|
||||
|
||||
.iframe_video {
|
||||
width: 527px !important;
|
||||
height: 536px !important;
|
||||
overflow: hidden !important;
|
||||
display: flex !important;
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
.animate_left {
|
||||
animation: left-move 1s ease-in-out;
|
||||
animation-fill-mode: forwards
|
||||
}
|
||||
|
||||
@keyframes left-move {
|
||||
to {
|
||||
transform: translateX(-131px);
|
||||
}
|
||||
}
|
||||
|
||||
.iframe_box {
|
||||
width: 527px;
|
||||
height: 357px;
|
||||
margin-top: 93px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.media_btn {
|
||||
margin-top: -63px;
|
||||
position: fixed;
|
||||
margin-left: 382px;
|
||||
}
|
||||
</style>
|
||||
42
addons/webman/views/my_editor.vue
Normal file
42
addons/webman/views/my_editor.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<v-md-editor
|
||||
v-model="value"
|
||||
:disabled-menus="[]"
|
||||
@upload-image="handleUploadImage"
|
||||
height="500px"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props:{
|
||||
value:String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: '',
|
||||
};
|
||||
},
|
||||
setup(props, ctx) {
|
||||
const value = Vueuse.useVModel(props, 'value',ctx.emit)
|
||||
return {
|
||||
value
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleUploadImage(event, insertImage, files) {
|
||||
// 此处只做示例
|
||||
const FormData1=new FormData()
|
||||
FormData1.append("file",files[0])
|
||||
this.$request.post("ex-admin/addons-webman-controller-IndexController/myEditorUpload", FormData1,{
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}).then(response=>{
|
||||
console.log(response.data)
|
||||
insertImage({
|
||||
url:response.data.url
|
||||
});
|
||||
})
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
350
addons/webman/views/socket.vue
Normal file
350
addons/webman/views/socket.vue
Normal file
@@ -0,0 +1,350 @@
|
||||
<template>
|
||||
<a-badge :count="count" showZeros="true">
|
||||
<a-button shape="circle" type="ghost" style="color:white" @click="showModal">
|
||||
<template #icon>
|
||||
<MessageOutlined/>
|
||||
</template>
|
||||
</a-button>
|
||||
</a-badge>
|
||||
<a-drawer
|
||||
v-model:visible="visible"
|
||||
:title="title"
|
||||
placement="right"
|
||||
:destroyOnClose="true"
|
||||
@close="closeDrawer()"
|
||||
width="500px"
|
||||
>
|
||||
<div>
|
||||
<div v-if="!timelineData.length && is_empty">
|
||||
<a-empty/>
|
||||
</div>
|
||||
<a-skeleton :loading="loading" active>
|
||||
<div v-if="timelineData.length" class="timeline-container" @scroll="handleScroll"
|
||||
style="height: 800px;overflow: scroll;overflow-x: hidden; padding: 10px">
|
||||
<a-timeline mode="alternate" pending="Recording...">
|
||||
<a-timeline-item v-for="item in timelineData" :key="item.id" :color="getColor(item.type)">
|
||||
<template #dot v-if="item.type === 6 || item.type === 5">
|
||||
<ClockCircleOutlined style="font-size: 16px"/>
|
||||
</template>
|
||||
<a-card hoverable size="small" bodyStyle="text-align:left" @click="pageJump(item.url, item.source_id)">
|
||||
<div style="display: inline-flex">
|
||||
<span style="font-size: 12px;width: 128px;line-height: 23px">{{ item.created_at }}</span>
|
||||
<a-tag v-if="item.type == 7" :color="item.status == 1 ? 'green' : 'red'"
|
||||
style="height: 23px;border-radius: 4px">
|
||||
{{ item.status == 1 ? online : untreated }}
|
||||
</a-tag>
|
||||
<a-tag v-else-if="item.type == 9 || item.type == 8 || item.type == 10"
|
||||
:color="item.machine_status == 0 ? 'red' : 'green'" style="height: 23px;border-radius: 4px">
|
||||
{{ item.machine_status == 0 ? untreated : processed }}
|
||||
</a-tag>
|
||||
<a-tag v-else-if="item.type == 20"
|
||||
:color="item.machine_status == 1 ? 'red' : 'green'" style="height: 23px;border-radius: 4px">
|
||||
{{ item.machine_status == 1 ? lock : open }}
|
||||
</a-tag>
|
||||
<a-tag v-else :color="item.type == 7 ? 'red' : 'green'" style="height: 23px;border-radius: 4px">
|
||||
{{ item.type == 7 ? offline : processed }}
|
||||
</a-tag>
|
||||
</div>
|
||||
<p style="font-weight: 700;margin-top: 5px">{{ item.title }}</p>
|
||||
<p style="font-size: 11px">{{ item.content }}</p>
|
||||
</a-card>
|
||||
</a-timeline-item>
|
||||
</a-timeline>
|
||||
</div>
|
||||
</a-skeleton>
|
||||
</div>
|
||||
</a-drawer>
|
||||
<audio controls="controls" hidden muted src="/audio/activity_examine.mp3" ref="activity_examine_audio"></audio>
|
||||
<audio controls="controls" hidden muted src="/audio/lottery_examine.mp3" ref="lottery_examine_audio"></audio>
|
||||
<audio controls="controls" hidden muted src="/audio/recharge_examine.mp3" ref="recharge_examine_audio"></audio>
|
||||
<audio controls="controls" hidden muted src="/audio/withdraw_examine.mp3" ref="withdraw_examine_audio"></audio>
|
||||
</template>
|
||||
<style>
|
||||
.action_content {
|
||||
height: 8px
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
const messages = {
|
||||
//简体中文
|
||||
'zh-CN': {
|
||||
message: {
|
||||
player_examine_recharge_order: '有新的充值订单需要审核!',
|
||||
player_create_withdraw_order: '有新的提现订单需要审核!',
|
||||
player_examine_activity_bonus: '当前存在待审核的活动奖励请尽快审核!',
|
||||
player_examine_lottery: '当前存在待审核的彩金奖励请尽快审核!',
|
||||
machine_online: '机台设备离线, 请尽快检查!',
|
||||
machine_lock: '机台设备上下分异常(锁定), 请尽快检查!',
|
||||
online: '在线',
|
||||
offline: '离线',
|
||||
processed: '已处理',
|
||||
untreated: '未处理',
|
||||
online_machine_info: '机台信息',
|
||||
lock: '锁定',
|
||||
open: '开启',
|
||||
}
|
||||
},
|
||||
//英文
|
||||
en: {
|
||||
message: {
|
||||
player_examine_recharge_order: 'There are new recharge orders that need to be reviewed!',
|
||||
player_create_withdraw_order: 'There are new withdrawal orders that need to be approved!',
|
||||
player_examine_activity_bonus: 'There are currently pending activity rewards for review. Please review them as soon as possible!',
|
||||
player_examine_lottery: 'There are currently lottery awards to be reviewed, please review as soon as possible!',
|
||||
machine_online: 'Machine equipment offline, please check as soon as possible!',
|
||||
machine_lock: 'The upper and lower parts of the machine equipment are abnormal (locked), please check as soon as possible!',
|
||||
online: 'on line',
|
||||
offline: 'off line',
|
||||
processed: 'processed',
|
||||
untreated: 'untreated',
|
||||
online_machine_info: 'Machine information',
|
||||
lock: 'Lock',
|
||||
open: 'Open',
|
||||
}
|
||||
},
|
||||
jp: {
|
||||
message: {
|
||||
player_examine_recharge_order: '新規チャージ注文がある場合はレビューが必要です',
|
||||
player_create_withdraw_order: '新規引出注文がある場合はレビューが必要です!',
|
||||
player_examine_activity_bonus: '現在レビュー対象のアクティビティインセンティブがあります。できるだけ早くレビューしてください!',
|
||||
player_examine_lottery: '現在レビュー対象のカラー報酬が存在します。できるだけ早くレビューしてください',
|
||||
machine_online: '机台設備がオフラインになっているので、できるだけ早くチェックしてください!',
|
||||
machine_lock: '机台設備の上下に異常(ロック)があるので、できるだけ早くチェックしてください!',
|
||||
online: 'オンライン',
|
||||
offline: 'オフライン',
|
||||
processed: '処理済み',
|
||||
untreated: '未処理',
|
||||
online_machine_info: 'きょくだいじょうほう',
|
||||
lock: 'Lock',
|
||||
open: 'Open',
|
||||
}
|
||||
},
|
||||
// 繁体中文
|
||||
'zh-TW': {
|
||||
message: {
|
||||
player_examine_recharge_order: '有新的充值訂單需要審核!',
|
||||
player_create_withdraw_order: '有新的提現訂單需要審核!',
|
||||
player_examine_activity_bonus: '當前存在待審核的活動獎勵請盡快審核!',
|
||||
player_examine_lottery: '當前存在待審核的彩金獎勵請盡快審核!',
|
||||
machine_online: '機台設備離線, 請盡快檢查!',
|
||||
machine_lock: '機台設備上下分异常(鎖定),請儘快檢查!',
|
||||
online: '在線',
|
||||
offline: '離線',
|
||||
processed: '已處理',
|
||||
untreated: '未處理',
|
||||
online_machine_info: '機台信息',
|
||||
lock: '鎖定',
|
||||
open: '開啟',
|
||||
}
|
||||
}
|
||||
}
|
||||
export default {
|
||||
name: "socket.vue",
|
||||
//可传参数
|
||||
props: {
|
||||
id: String,
|
||||
type: String,
|
||||
department_id: String,
|
||||
count: String,
|
||||
lang: String,
|
||||
topShow: String,
|
||||
ws: String,
|
||||
examine_withdraw: String,
|
||||
examine_recharge: String,
|
||||
examine_activity: String,
|
||||
examine_lottery: String,
|
||||
machine: String,
|
||||
title: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
timelineData: [],
|
||||
page: 1,
|
||||
size: 20,
|
||||
is_empty: false,
|
||||
loading: true,
|
||||
online: '',
|
||||
offline: '',
|
||||
open: '',
|
||||
lock: '',
|
||||
processed: '',
|
||||
untreated: '',
|
||||
connection: null
|
||||
};
|
||||
},
|
||||
//生命周期渲染完执行
|
||||
created() {
|
||||
this.online = messages[this.lang].message.online;
|
||||
this.offline = messages[this.lang].message.offline;
|
||||
this.processed = messages[this.lang].message.processed;
|
||||
this.untreated = messages[this.lang].message.untreated;
|
||||
this.lock = messages[this.lang].message.lock;
|
||||
this.open = messages[this.lang].message.open;
|
||||
let this_p = this;
|
||||
// 初始化时间线的初始数据
|
||||
if (this.ws && (this.examine_withdraw === true || this.examine_recharge === true || this.examine_activity === true || this.examine_lottery === true || this.machine === true)) {
|
||||
this.$script('/plugin/webman/push/push.js').then(() => {
|
||||
this_p.connection = new Push({
|
||||
url: this.ws, // websocket地址
|
||||
app_key: '20f94408fc4c52845f162e92a253c7a3',
|
||||
auth: '/plugin/webman/push/auth' // 订阅鉴权(仅限于私有频道)
|
||||
});
|
||||
let lang = this.lang;
|
||||
let admin_id = this.id;
|
||||
let type = this.type;
|
||||
let examine_withdraw = this.examine_withdraw;
|
||||
let examine_recharge = this.examine_recharge;
|
||||
let department_id = this.department_id;
|
||||
let admin_channel = this_p.connection.subscribe('private-' + type + '-' + department_id + '-' + admin_id);
|
||||
let group_channel = this_p.connection.subscribe('private-admin_group-' + type + '-' + department_id);
|
||||
let that = this;
|
||||
let title = '';
|
||||
let router = '';
|
||||
let params = '';
|
||||
let description = '';
|
||||
admin_channel.on('message', function (data) {
|
||||
let content = JSON.parse(data.content);
|
||||
switch (content.msg_type) {
|
||||
case 'machine_action_result':
|
||||
that.$notification.info({
|
||||
message: messages[lang].message.online_machine_info,
|
||||
description: content.description.split('\n').map((paragraph) => {
|
||||
return Vue.createVNode('p', {class: 'action_content'}, paragraph);
|
||||
}),
|
||||
});
|
||||
break;
|
||||
default:
|
||||
that.openNotification(title, router, description, params);
|
||||
break;
|
||||
}
|
||||
});
|
||||
group_channel.on('message', function (data) {
|
||||
let content = JSON.parse(data.content);
|
||||
switch (content.msg_type) {
|
||||
case 'player_create_withdraw_order':
|
||||
if (examine_withdraw === true) {
|
||||
title = messages[lang].message.player_create_withdraw_order;
|
||||
router = '/ex-admin/addons-webman-controller-ChannelWithdrawRecordController/examineList';
|
||||
params = content.tradeno
|
||||
// 语言播报
|
||||
that.startPlay('withdraw_examine');
|
||||
that.openNotification(title, router, description, params);
|
||||
}
|
||||
break;
|
||||
case 'player_examine_recharge_order':
|
||||
if (examine_recharge === true) {
|
||||
title = messages[lang].message.player_examine_recharge_order;
|
||||
router = '/ex-admin/addons-webman-controller-ChannelRechargeRecordController/examineList';
|
||||
params = content.tradeno
|
||||
// 语言播报
|
||||
that.startPlay('recharge_examine');
|
||||
that.openNotification(title, router, description, params);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.connection) {
|
||||
this.connection.disconnect();
|
||||
this.connection = null;
|
||||
}
|
||||
},
|
||||
//定义函数方法
|
||||
methods: {
|
||||
openNotification(title, router, description = '', params) {
|
||||
this.$notification.info({
|
||||
message: title,
|
||||
description: description,
|
||||
onClick: () => {
|
||||
this.$router.push({path: router, query: {tradeno: params}})
|
||||
},
|
||||
});
|
||||
},
|
||||
openNotificationErro(title, router, description = '', params) {
|
||||
this.$notification.error({
|
||||
message: title,
|
||||
description: description,
|
||||
onClick: () => {
|
||||
this.$router.push({path: router, query: {tradeno: params}})
|
||||
},
|
||||
});
|
||||
},
|
||||
async startPlay(v) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs[`${v}_audio`].muted = false;
|
||||
this.$refs[`${v}_audio`].currentTime = 0;
|
||||
this.$refs[`${v}_audio`].play();
|
||||
})
|
||||
},
|
||||
async startPlayLottery() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.lottery_examine_audio.muted = false;
|
||||
this.$refs.lottery_examine_audio.currentTime = 0;
|
||||
this.$refs.lottery_examine_audio.play();
|
||||
})
|
||||
},
|
||||
showModal() {
|
||||
this.visible = true;
|
||||
this.loadMore();
|
||||
},
|
||||
closeDrawer() {
|
||||
this.timelineData = [];
|
||||
this.page = 1;
|
||||
this.is_empty = false;
|
||||
this.loading = true;
|
||||
},
|
||||
handleScroll() {
|
||||
const container = document.querySelector('.timeline-container');
|
||||
if (container.scrollTop + container.clientHeight >= container.scrollHeight) {
|
||||
this.page = this.page + 1;
|
||||
this.loadMore();
|
||||
}
|
||||
},
|
||||
loadMore() {
|
||||
this.$request({
|
||||
url: 'ex-admin/system/noticeList',
|
||||
method: 'post',
|
||||
data: {
|
||||
'page': this.page,
|
||||
'size': this.size,
|
||||
},
|
||||
}).then(response => {
|
||||
this.loading = false;
|
||||
if (response.data.length > 0) {
|
||||
this.timelineData = this.timelineData.concat(response.data);
|
||||
} else {
|
||||
this.is_empty = true;
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
},
|
||||
getColor(type) {
|
||||
switch (type) {
|
||||
case 3:
|
||||
return 'green'
|
||||
case 4:
|
||||
return 'gray'
|
||||
case 5:
|
||||
return 'green'
|
||||
case 6:
|
||||
return 'orange'
|
||||
case 7:
|
||||
return 'red'
|
||||
}
|
||||
},
|
||||
pageJump(url, source_id) {
|
||||
this.closeDrawer()
|
||||
this.visible = false;
|
||||
this.$router.push({
|
||||
path: '/' + url,
|
||||
params: {source_id: source_id}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user