1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-31 14:08:06 +08:00

feat: Adapt to internationalization (#7205)

This commit is contained in:
2024-11-28 18:40:39 +08:00 committed by GitHub
parent ae03b85a97
commit 83ce744fb2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 219 additions and 149 deletions

View File

@ -130,6 +130,15 @@ func (b *BaseApi) CheckIsDemo(c *gin.Context) {
helper.SuccessWithData(c, global.CONF.System.IsDemo) helper.SuccessWithData(c, global.CONF.System.IsDemo)
} }
// @Tags Auth
// @Summary Check System isDemo
// @Description 判断是否为国际版
// @Success 200
// @Router /auth/intl [get]
func (b *BaseApi) CheckIsIntl(c *gin.Context) {
helper.SuccessWithData(c, global.CONF.System.IsIntl)
}
// @Tags Auth // @Tags Auth
// @Summary Load System Language // @Summary Load System Language
// @Description 获取系统语言设置 // @Description 获取系统语言设置

View File

@ -20,7 +20,9 @@ type System struct {
Username string `mapstructure:"username"` Username string `mapstructure:"username"`
Password string `mapstructure:"password"` Password string `mapstructure:"password"`
Entrance string `mapstructure:"entrance"` Entrance string `mapstructure:"entrance"`
Language string `mapstructure:"language"`
IsDemo bool `mapstructure:"is_demo"` IsDemo bool `mapstructure:"is_demo"`
IsIntl bool `mapstructure:"is_intl"`
AppRepo string `mapstructure:"app_repo"` AppRepo string `mapstructure:"app_repo"`
ChangeUserInfo string `mapstructure:"change_user_info"` ChangeUserInfo string `mapstructure:"change_user_info"`
OneDriveID string `mapstructure:"one_drive_id"` OneDriveID string `mapstructure:"one_drive_id"`

View File

@ -72,6 +72,11 @@ var AddTableSetting = &gormigrate.Migration{
} }
global.CONF.System.EncryptKey = encryptKey global.CONF.System.EncryptKey = encryptKey
pass, _ := encrypt.StringEncrypt(global.CONF.System.Password) pass, _ := encrypt.StringEncrypt(global.CONF.System.Password)
language := "en"
if global.CONF.System.Language == "zh" {
language = "zh"
}
if err := tx.Create(&model.Setting{Key: "Password", Value: pass}).Error; err != nil { if err := tx.Create(&model.Setting{Key: "Password", Value: pass}).Error; err != nil {
return err return err
} }
@ -82,7 +87,7 @@ var AddTableSetting = &gormigrate.Migration{
if err := tx.Create(&model.Setting{Key: "PanelName", Value: "1Panel"}).Error; err != nil { if err := tx.Create(&model.Setting{Key: "PanelName", Value: "1Panel"}).Error; err != nil {
return err return err
} }
if err := tx.Create(&model.Setting{Key: "Language", Value: "zh"}).Error; err != nil { if err := tx.Create(&model.Setting{Key: "Language", Value: language}).Error; err != nil {
return err return err
} }
if err := tx.Create(&model.Setting{Key: "Theme", Value: "light"}).Error; err != nil { if err := tx.Create(&model.Setting{Key: "Theme", Value: "light"}).Error; err != nil {

View File

@ -21,7 +21,7 @@ func Init() {
port := "9999" port := "9999"
mode := "" mode := ""
version := "v1.0.0" version := "v1.0.0"
username, password, entrance := "", "", "" username, password, entrance, language := "", "", "", "zh"
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
v := viper.NewWithOptions() v := viper.NewWithOptions()
v.SetConfigType("yaml") v.SetConfigType("yaml")
@ -46,6 +46,7 @@ func Init() {
username = loadParams("ORIGINAL_USERNAME") username = loadParams("ORIGINAL_USERNAME")
password = loadParams("ORIGINAL_PASSWORD") password = loadParams("ORIGINAL_PASSWORD")
entrance = loadParams("ORIGINAL_ENTRANCE") entrance = loadParams("ORIGINAL_ENTRANCE")
language = loadParams("LANGUAGE")
reader := bytes.NewReader(conf.AppYaml) reader := bytes.NewReader(conf.AppYaml)
if err := v.ReadConfig(reader); err != nil { if err := v.ReadConfig(reader); err != nil {
@ -80,11 +81,15 @@ func Init() {
if serverConfig.System.Entrance != "" { if serverConfig.System.Entrance != "" {
entrance = serverConfig.System.Entrance entrance = serverConfig.System.Entrance
} }
if serverConfig.System.IsIntl {
language = "en"
}
} }
global.CONF = serverConfig global.CONF = serverConfig
global.CONF.System.BaseDir = baseDir global.CONF.System.BaseDir = baseDir
global.CONF.System.IsDemo = v.GetBool("system.is_demo") global.CONF.System.IsDemo = v.GetBool("system.is_demo")
global.CONF.System.IsIntl = v.GetBool("system.is_intl")
global.CONF.System.DataDir = path.Join(global.CONF.System.BaseDir, "1panel") global.CONF.System.DataDir = path.Join(global.CONF.System.BaseDir, "1panel")
global.CONF.System.Cache = path.Join(global.CONF.System.DataDir, "cache") global.CONF.System.Cache = path.Join(global.CONF.System.DataDir, "cache")
global.CONF.System.Backup = path.Join(global.CONF.System.DataDir, "backup") global.CONF.System.Backup = path.Join(global.CONF.System.DataDir, "backup")
@ -96,6 +101,7 @@ func Init() {
global.CONF.System.Username = username global.CONF.System.Username = username
global.CONF.System.Password = password global.CONF.System.Password = password
global.CONF.System.Entrance = entrance global.CONF.System.Entrance = entrance
global.CONF.System.Language = language
global.CONF.System.ChangeUserInfo = loadChangeInfo() global.CONF.System.ChangeUserInfo = loadChangeInfo()
global.Viper = v global.Viper = v
} }

View File

@ -17,5 +17,6 @@ func (s *BaseRouter) InitRouter(Router *gin.RouterGroup) {
baseRouter.POST("/logout", baseApi.LogOut) baseRouter.POST("/logout", baseApi.LogOut)
baseRouter.GET("/demo", baseApi.CheckIsDemo) baseRouter.GET("/demo", baseApi.CheckIsDemo)
baseRouter.GET("/language", baseApi.GetLanguage) baseRouter.GET("/language", baseApi.GetLanguage)
baseRouter.GET("/intl", baseApi.CheckIsIntl)
} }
} }

View File

@ -5,6 +5,7 @@ system:
repo_url: https://resource.fit2cloud.com/1panel/package repo_url: https://resource.fit2cloud.com/1panel/package
app_repo: https://apps-assets.fit2cloud.com app_repo: https://apps-assets.fit2cloud.com
is_demo: false is_demo: false
is_intl: true
port: 9999 port: 9999
username: admin username: admin
password: admin123 password: admin123

View File

@ -24,3 +24,7 @@ export const checkIsDemo = () => {
export const getLanguage = () => { export const getLanguage = () => {
return http.get<string>(`/auth/language`); return http.get<string>(`/auth/language`);
}; };
export const checkIsIntl = () => {
return http.get<boolean>('/auth/intl');
};

View File

@ -5,8 +5,8 @@
<el-link type="primary" :underline="false" @click="toForum"> <el-link type="primary" :underline="false" @click="toForum">
<span class="font-normal">{{ $t('setting.forum') }}</span> <span class="font-normal">{{ $t('setting.forum') }}</span>
</el-link> </el-link>
<el-divider direction="vertical" /> <el-divider direction="vertical" v-if="!globalStore.isIntl" />
<el-link type="primary" :underline="false" @click="toDoc"> <el-link type="primary" :underline="false" @click="toDoc" v-if="!globalStore.isIntl">
<span class="font-normal">{{ $t('setting.doc2') }}</span> <span class="font-normal">{{ $t('setting.doc2') }}</span>
</el-link> </el-link>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
@ -17,7 +17,7 @@
</div> </div>
<div class="flex flex-wrap items-center"> <div class="flex flex-wrap items-center">
<el-link :underline="false" type="primary" @click="toHalo"> <el-link :underline="false" type="primary" @click="toHalo">
{{ isProductPro ? $t('license.pro') : $t('license.community') }} {{ isProductPro && globalStore.isIntl ? $t('license.pro') : $t('license.community') }}
</el-link> </el-link>
<el-link :underline="false" class="version" type="primary" @click="copyText(version)"> <el-link :underline="false" class="version" type="primary" @click="copyText(version)">
{{ version }} {{ version }}
@ -122,7 +122,9 @@ const handleClose = () => {
}; };
const toHalo = () => { const toHalo = () => {
window.open('https://www.lxware.cn/1panel' + '', '_blank', 'noopener,noreferrer'); if (!globalStore.isIntl) {
window.open('https://www.lxware.cn/1panel' + '', '_blank', 'noopener,noreferrer');
}
}; };
const toDoc = () => { const toDoc = () => {
@ -130,7 +132,10 @@ const toDoc = () => {
}; };
const toForum = () => { const toForum = () => {
window.open('https://bbs.fit2cloud.com/c/1p/7', '_blank'); let url = globalStore.isIntl
? 'https://github.com/1Panel-dev/1Panel/discussions'
: 'https://bbs.fit2cloud.com/c/1p/7';
window.open(url, '_blank');
}; };
const toGithub = () => { const toGithub = () => {

View File

@ -344,6 +344,8 @@ const message = {
kernelArch: 'Kernel arch', kernelArch: 'Kernel arch',
network: 'Network', network: 'Network',
io: 'Disk IO', io: 'Disk IO',
ip: 'Host Address',
proxy: 'System Proxy',
baseInfo: 'Base info', baseInfo: 'Base info',
totalSend: 'Total send', totalSend: 'Total send',
totalRecv: 'Total recv', totalRecv: 'Total recv',

View File

@ -42,7 +42,7 @@ import Logo from './components/Logo.vue';
import Collapse from './components/Collapse.vue'; import Collapse from './components/Collapse.vue';
import SubItem from './components/SubItem.vue'; import SubItem from './components/SubItem.vue';
import router, { menuList } from '@/routers/router'; import router, { menuList } from '@/routers/router';
import { logOutApi } from '@/api/modules/auth'; import { checkIsIntl, logOutApi } from '@/api/modules/auth';
import i18n from '@/lang'; import i18n from '@/lang';
import { ElMessageBox } from 'element-plus'; import { ElMessageBox } from 'element-plus';
import { GlobalStore, MenuStore } from '@/store'; import { GlobalStore, MenuStore } from '@/store';
@ -68,7 +68,7 @@ const activeMenu = computed(() => {
const isCollapse = computed((): boolean => menuStore.isCollapse); const isCollapse = computed((): boolean => menuStore.isCollapse);
let routerMenus = computed((): RouteRecordRaw[] => { let routerMenus = computed((): RouteRecordRaw[] => {
return menuStore.menuList.filter((route) => route.meta && !route.meta.hideInSidebar); return menuStore.menuList.filter((route) => route.meta && !route.meta.hideInSidebar) as RouteRecordRaw[];
}); });
const screenWidth = ref(0); const screenWidth = ref(0);
@ -132,9 +132,14 @@ function getCheckedLabels(json: Node): string[] {
} }
const search = async () => { const search = async () => {
const res = await getSettingInfo(); await checkIsSystemIntl();
const json: Node = JSON.parse(res.data.xpackHideMenu); let checkedLabels: string | any[];
const checkedLabels = getCheckedLabels(json); if (!globalStore.isIntl) {
const res = await getSettingInfo();
const json: Node = JSON.parse(res.data.xpackHideMenu);
checkedLabels = getCheckedLabels(json);
}
let rstMenuList: RouteRecordRaw[] = []; let rstMenuList: RouteRecordRaw[] = [];
menuStore.menuList.forEach((item) => { menuStore.menuList.forEach((item) => {
let menuItem = JSON.parse(JSON.stringify(item)); let menuItem = JSON.parse(JSON.stringify(item));
@ -173,6 +178,11 @@ const search = async () => {
menuStore.menuList = rstMenuList; menuStore.menuList = rstMenuList;
}; };
const checkIsSystemIntl = async () => {
const res = await checkIsIntl();
globalStore.isIntl = res.data;
};
onMounted(() => { onMounted(() => {
menuStore.setMenuList(menuList); menuStore.setMenuList(menuList);
search(); search();

View File

@ -34,6 +34,7 @@ export interface GlobalState {
defaultNetwork: string; defaultNetwork: string;
isProductPro: boolean; isProductPro: boolean;
isIntl: boolean;
isTrial: boolean; isTrial: boolean;
productProExpires: number; productProExpires: number;

View File

@ -37,6 +37,7 @@ const GlobalStore = defineStore({
defaultNetwork: 'all', defaultNetwork: 'all',
isProductPro: false, isProductPro: false,
isIntl: false,
isTrial: false, isTrial: false,
productProExpires: 0, productProExpires: 0,

View File

@ -320,30 +320,32 @@
</el-form-item> </el-form-item>
</div> </div>
<el-form-item prop="hasAlert"> <div v-if="!globalStore.isIntl">
<el-checkbox v-model="dialogData.rowData!.hasAlert" :label="$t('alert.isAlert')" /> <el-form-item prop="hasAlert">
<span class="input-help">{{ $t('alert.cronJobHelper') }}</span> <el-checkbox v-model="dialogData.rowData!.hasAlert" :label="$t('alert.isAlert')" />
</el-form-item> <span class="input-help">{{ $t('alert.cronJobHelper') }}</span>
<el-form-item </el-form-item>
prop="alertCount" <el-form-item
v-if="dialogData.rowData!.hasAlert && isProductPro" prop="alertCount"
:label="$t('alert.alertCount')" v-if="dialogData.rowData!.hasAlert && isProductPro"
> :label="$t('alert.alertCount')"
<el-input-number >
style="width: 200px" <el-input-number
:min="1" style="width: 200px"
step-strictly :min="1"
:step="1" step-strictly
v-model.number="dialogData.rowData!.alertCount" :step="1"
></el-input-number> v-model.number="dialogData.rowData!.alertCount"
<span class="input-help">{{ $t('alert.alertCountHelper') }}</span> ></el-input-number>
</el-form-item> <span class="input-help">{{ $t('alert.alertCountHelper') }}</span>
<el-form-item v-if="dialogData.rowData!.hasAlert && !isProductPro"> </el-form-item>
<span>{{ $t('alert.licenseHelper') }}</span> <el-form-item v-if="dialogData.rowData!.hasAlert && !isProductPro">
<el-button link type="primary" @click="toUpload"> <span>{{ $t('alert.licenseHelper') }}</span>
{{ $t('license.levelUpPro') }} <el-button link type="primary" @click="toUpload">
</el-button> {{ $t('license.levelUpPro') }}
</el-form-item> </el-button>
</el-form-item>
</div>
<el-form-item :label="$t('cronjob.retainCopies')" prop="retainCopies"> <el-form-item :label="$t('cronjob.retainCopies')" prop="retainCopies">
<el-input-number <el-input-number

View File

@ -9,12 +9,10 @@
]" ]"
> >
<template #route-button> <template #route-button>
<div class="router-button"> <div class="router-button" v-if="!isProductPro && !globalStore.isIntl">
<template v-if="!isProductPro"> <el-button link type="primary" @click="toUpload">
<el-button link type="primary" @click="toUpload"> {{ $t('license.levelUpPro') }}
{{ $t('license.levelUpPro') }} </el-button>
</el-button>
</template>
</div> </div>
</template> </template>
</RouterButton> </RouterButton>

View File

@ -120,22 +120,24 @@
{{ $t('commons.button.login') }} {{ $t('commons.button.login') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
<el-form-item prop="agreeLicense"> <template v-if="!isIntl">
<el-checkbox v-model="loginForm.agreeLicense"> <el-form-item prop="agreeLicense">
<template #default> <el-checkbox v-model="loginForm.agreeLicense">
<span class="agree" v-html="$t('commons.login.licenseHelper')"></span> <template #default>
</template> <span class="agree" v-html="$t('commons.login.licenseHelper')"></span>
</el-checkbox> </template>
</el-form-item> </el-checkbox>
<div class="agree-helper"> </el-form-item>
<span <div class="agree-helper">
v-if="!loginForm.agreeLicense && !_isMobile()" <span
class="input-error" v-if="!loginForm.agreeLicense && !_isMobile()"
style="line-height: 14px" class="input-error"
> style="line-height: 14px"
{{ $t('commons.login.errorAgree') }} >
</span> {{ $t('commons.login.errorAgree') }}
</div> </span>
</div>
</template>
</el-form> </el-form>
<div class="demo"> <div class="demo">
<span v-if="isDemo"> <span v-if="isDemo">
@ -171,7 +173,7 @@
import { ref, reactive, onMounted, computed } from 'vue'; import { ref, reactive, onMounted, computed } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import type { ElForm } from 'element-plus'; import type { ElForm } from 'element-plus';
import { loginApi, getCaptcha, mfaLoginApi, checkIsDemo, getLanguage } from '@/api/modules/auth'; import { loginApi, getCaptcha, mfaLoginApi, checkIsDemo, getLanguage, checkIsIntl } from '@/api/modules/auth';
import { GlobalStore, MenuStore, TabsStore } from '@/store'; import { GlobalStore, MenuStore, TabsStore } from '@/store';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@ -188,6 +190,7 @@ const errAuthInfo = ref(false);
const errCaptcha = ref(false); const errCaptcha = ref(false);
const errMfaInfo = ref(false); const errMfaInfo = ref(false);
const isDemo = ref(false); const isDemo = ref(false);
const isIntl = ref(true);
const agreeVisible = ref(false); const agreeVisible = ref(false);
type FormInstance = InstanceType<typeof ElForm>; type FormInstance = InstanceType<typeof ElForm>;
@ -235,6 +238,12 @@ const mfaShow = ref<boolean>(false);
const router = useRouter(); const router = useRouter();
const dropdownText = ref('中文(简体)'); const dropdownText = ref('中文(简体)');
const checkIsSystemIntl = async () => {
const res = await checkIsIntl();
isIntl.value = res.data;
globalStore.isIntl = isIntl.value;
};
function handleCommand(command: string) { function handleCommand(command: string) {
loginForm.language = command; loginForm.language = command;
usei18n.locale.value = command; usei18n.locale.value = command;
@ -258,6 +267,9 @@ const login = (formEl: FormInstance | undefined) => {
if (!formEl || isLoggingIn) return; if (!formEl || isLoggingIn) return;
formEl.validate(async (valid) => { formEl.validate(async (valid) => {
if (!valid) return; if (!valid) return;
if (isIntl.value) {
loginForm.agreeLicense = true;
}
if (!loginForm.agreeLicense) { if (!loginForm.agreeLicense) {
if (_isMobile()) { if (_isMobile()) {
agreeVisible.value = true; agreeVisible.value = true;
@ -372,6 +384,7 @@ const loadDataFromDB = async () => {
onMounted(() => { onMounted(() => {
globalStore.isOnRestart = false; globalStore.isOnRestart = false;
checkIsSystemIntl();
loginVerify(); loginVerify();
loadLanguage(); loadLanguage();
document.title = globalStore.themeConfig.panelName; document.title = globalStore.themeConfig.panelName;

View File

@ -16,7 +16,7 @@
<SystemUpgrade /> <SystemUpgrade />
</div> </div>
<div class="flex w-full justify-center my-5 flex-wrap md:flex-row gap-4"> <div class="flex w-full justify-center my-5 flex-wrap md:flex-row gap-4">
<el-link @click="toDoc" class="system-link"> <el-link @click="toDoc" class="system-link" v-if="!globalStore.isIntl">
<el-icon><Document /></el-icon> <el-icon><Document /></el-icon>
<span>{{ $t('setting.doc2') }}</span> <span>{{ $t('setting.doc2') }}</span>
</el-link> </el-link>

View File

@ -9,6 +9,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import i18n from '@/lang'; import i18n from '@/lang';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
const buttons = [ const buttons = [
{ {
@ -36,4 +38,8 @@ const buttons = [
path: '/settings/about', path: '/settings/about',
}, },
]; ];
if (globalStore.isIntl) {
buttons.splice(4, 1);
}
</script> </script>

View File

@ -123,7 +123,7 @@
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.proxy')" prop="proxyShow"> <el-form-item v-if="!globalStore.isIntl" :label="$t('setting.proxy')" prop="proxyShow">
<el-input disabled v-model="form.proxyShow"> <el-input disabled v-model="form.proxyShow">
<template #append> <template #append>
<el-button @click="onChangeProxy" icon="Setting"> <el-button @click="onChangeProxy" icon="Setting">
@ -158,7 +158,7 @@
<span class="input-help">{{ $t('setting.developerModeHelper') }}</span> <span class="input-help">{{ $t('setting.developerModeHelper') }}</span>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.advancedMenuHide')"> <el-form-item v-if="!globalStore.isIntl" :label="$t('setting.advancedMenuHide')">
<el-input disabled v-model="form.proHideMenus"> <el-input disabled v-model="form.proHideMenus">
<template #append> <template #append>
<el-button v-show="!show" @click="onChangeHideMenus" icon="Setting"> <el-button v-show="!show" @click="onChangeHideMenus" icon="Setting">

View File

@ -50,95 +50,99 @@
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="hasSpec"> <div v-if="!globalStore.isIntl">
<el-checkbox v-model="dialogData.rowData!.hasSpec" :label="$t('toolbox.clam.cron')" /> <el-form-item prop="hasSpec">
</el-form-item> <el-checkbox v-model="dialogData.rowData!.hasSpec" :label="$t('toolbox.clam.cron')" />
<el-form-item prop="spec" v-if="dialogData.rowData!.hasSpec && isProductPro"> </el-form-item>
<el-select <el-form-item prop="spec" v-if="dialogData.rowData!.hasSpec && isProductPro">
class="specTypeClass" <el-select
v-model="dialogData.rowData!.specObj.specType" class="specTypeClass"
@change="changeSpecType()" v-model="dialogData.rowData!.specObj.specType"
@change="changeSpecType()"
>
<el-option
v-for="item in specOptions"
:key="item.label"
:value="item.value"
:label="item.label"
/>
</el-select>
<el-select
v-if="dialogData.rowData!.specObj.specType === 'perWeek'"
class="specClass"
v-model="dialogData.rowData!.specObj.week"
>
<el-option
v-for="item in weekOptions"
:key="item.label"
:value="item.value"
:label="item.label"
/>
</el-select>
<el-input
v-if="hasDay(dialogData.rowData!.specObj)"
class="specClass"
v-model.number="dialogData.rowData!.specObj.day"
>
<template #append>
<div class="append">{{ $t('cronjob.day') }}</div>
</template>
</el-input>
<el-input
v-if="hasHour(dialogData.rowData!.specObj)"
class="specClass"
v-model.number="dialogData.rowData!.specObj.hour"
>
<template #append>
<div class="append">{{ $t('commons.units.hour') }}</div>
</template>
</el-input>
<el-input
v-if="dialogData.rowData!.specObj.specType !== 'perNSecond'"
class="specClass"
v-model.number="dialogData.rowData!.specObj.minute"
>
<template #append>
<div class="append">{{ $t('commons.units.minute') }}</div>
</template>
</el-input>
<el-input
v-if="dialogData.rowData!.specObj.specType === 'perNSecond'"
class="specClass"
v-model.number="dialogData.rowData!.specObj.second"
>
<template #append>
<div class="append">{{ $t('commons.units.second') }}</div>
</template>
</el-input>
</el-form-item>
<el-form-item prop="hasAlert">
<el-checkbox v-model="dialogData.rowData!.hasAlert" :label="$t('alert.isAlert')" />
<span class="input-help">{{ $t('alert.clamHelper') }}</span>
</el-form-item>
<el-form-item
v-if="(dialogData.rowData!.hasAlert || dialogData.rowData!.hasSpec) && !isProductPro"
> >
<el-option <span>{{ $t('toolbox.clam.alertHelper') }}</span>
v-for="item in specOptions" <el-button link type="primary" @click="toUpload">
:key="item.label" {{ $t('license.levelUpPro') }}
:value="item.value" </el-button>
:label="item.label" </el-form-item>
/> <el-form-item
</el-select> prop="alertCount"
<el-select v-if="dialogData.rowData!.hasAlert && isProductPro"
v-if="dialogData.rowData!.specObj.specType === 'perWeek'" :label="$t('alert.alertCount')"
class="specClass"
v-model="dialogData.rowData!.specObj.week"
> >
<el-option <el-input-number
v-for="item in weekOptions" style="width: 200px"
:key="item.label" :min="1"
:value="item.value" step-strictly
:label="item.label" :step="1"
/> v-model.number="dialogData.rowData!.alertCount"
</el-select> ></el-input-number>
<el-input <span class="input-help">{{ $t('alert.alertCountHelper') }}</span>
v-if="hasDay(dialogData.rowData!.specObj)" </el-form-item>
class="specClass" </div>
v-model.number="dialogData.rowData!.specObj.day"
>
<template #append>
<div class="append">{{ $t('cronjob.day') }}</div>
</template>
</el-input>
<el-input
v-if="hasHour(dialogData.rowData!.specObj)"
class="specClass"
v-model.number="dialogData.rowData!.specObj.hour"
>
<template #append>
<div class="append">{{ $t('commons.units.hour') }}</div>
</template>
</el-input>
<el-input
v-if="dialogData.rowData!.specObj.specType !== 'perNSecond'"
class="specClass"
v-model.number="dialogData.rowData!.specObj.minute"
>
<template #append>
<div class="append">{{ $t('commons.units.minute') }}</div>
</template>
</el-input>
<el-input
v-if="dialogData.rowData!.specObj.specType === 'perNSecond'"
class="specClass"
v-model.number="dialogData.rowData!.specObj.second"
>
<template #append>
<div class="append">{{ $t('commons.units.second') }}</div>
</template>
</el-input>
</el-form-item>
<el-form-item prop="hasAlert">
<el-checkbox v-model="dialogData.rowData!.hasAlert" :label="$t('alert.isAlert')" />
<span class="input-help">{{ $t('alert.clamHelper') }}</span>
</el-form-item>
<el-form-item v-if="(dialogData.rowData!.hasAlert || dialogData.rowData!.hasSpec) && !isProductPro">
<span>{{ $t('toolbox.clam.alertHelper') }}</span>
<el-button link type="primary" @click="toUpload">
{{ $t('license.levelUpPro') }}
</el-button>
</el-form-item>
<el-form-item
prop="alertCount"
v-if="dialogData.rowData!.hasAlert && isProductPro"
:label="$t('alert.alertCount')"
>
<el-input-number
style="width: 200px"
:min="1"
step-strictly
:step="1"
v-model.number="dialogData.rowData!.alertCount"
></el-input-number>
<span class="input-help">{{ $t('alert.alertCountHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('commons.table.description')" prop="description"> <el-form-item :label="$t('commons.table.description')" prop="description">
<el-input type="textarea" :rows="3" clearable v-model="dialogData.rowData!.description" /> <el-input type="textarea" :rows="3" clearable v-model="dialogData.rowData!.description" />
</el-form-item> </el-form-item>