refactor: install page. (#296)

* refactor: install page.

* refactor: install page.

* refactor: install page.

* refactor: install page.
pull/300/head
Ryan Wang 2021-02-25 20:33:20 +08:00 committed by GitHub
parent 3799afcdd3
commit acbbb19347
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 306 additions and 259 deletions

31
package-lock.json generated
View File

@ -1351,9 +1351,9 @@
}, },
"dependencies": { "dependencies": {
"core-js": { "core-js": {
"version": "3.8.2", "version": "3.9.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.2.tgz", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.9.0.tgz",
"integrity": "sha512-FfApuSRgrR6G5s58casCBd9M2k+4ikuu4wbW6pJyYU7bd9zvFc9qf7vr5xmrZOhT9nn+8uwlH1oRR9jTnFoA3A==" "integrity": "sha512-PyFBJaLq93FlyYdsndE5VaueA9K5cNB7CGzeCj191YYLhkQM0gdZR2SKihM70oF0wdqKSKClv/tEBOpoRmdOVQ=="
} }
} }
}, },
@ -3113,9 +3113,9 @@
} }
}, },
"ant-design-vue": { "ant-design-vue": {
"version": "1.7.2", "version": "1.7.3",
"resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-1.7.2.tgz", "resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-1.7.3.tgz",
"integrity": "sha512-iVskTSG62OSiptyGQkvyhoeLlLMiqKtAOTcWZ7MmsMrj38h0TCpGtqSYS4/fwq4yYgyzloYSteBo8U8TrV99RA==", "integrity": "sha512-OuJBZmpcy32OvdvP7iW1EqdqU6l/dta1zcQ/U0fep7EGksPviVK7ssifI5QMZFQDDsti0dYQhCEOZWUG0Y/xxQ==",
"requires": { "requires": {
"@ant-design/icons": "^2.1.1", "@ant-design/icons": "^2.1.1",
"@ant-design/icons-vue": "^2.0.0", "@ant-design/icons-vue": "^2.0.0",
@ -5382,7 +5382,7 @@
}, },
"component-classes": { "component-classes": {
"version": "1.2.6", "version": "1.2.6",
"resolved": "https://registry.npm.taobao.org/component-classes/download/component-classes-1.2.6.tgz", "resolved": "https://registry.npmjs.org/component-classes/-/component-classes-1.2.6.tgz",
"integrity": "sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE=", "integrity": "sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE=",
"requires": { "requires": {
"component-indexof": "0.0.3" "component-indexof": "0.0.3"
@ -5396,7 +5396,7 @@
}, },
"component-indexof": { "component-indexof": {
"version": "0.0.3", "version": "0.0.3",
"resolved": "https://registry.npm.taobao.org/component-indexof/download/component-indexof-0.0.3.tgz", "resolved": "https://registry.npmjs.org/component-indexof/-/component-indexof-0.0.3.tgz",
"integrity": "sha1-EdCRMSI5648yyPJa6csAL/6NPCQ=" "integrity": "sha1-EdCRMSI5648yyPJa6csAL/6NPCQ="
}, },
"compressible": { "compressible": {
@ -6519,7 +6519,7 @@
}, },
"dom-closest": { "dom-closest": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npm.taobao.org/dom-closest/download/dom-closest-0.2.0.tgz", "resolved": "https://registry.npmjs.org/dom-closest/-/dom-closest-0.2.0.tgz",
"integrity": "sha1-69n5HRvyLo1vR3h2u80+yQIWwM8=", "integrity": "sha1-69n5HRvyLo1vR3h2u80+yQIWwM8=",
"requires": { "requires": {
"dom-matches": ">=1.0.1" "dom-matches": ">=1.0.1"
@ -6542,7 +6542,7 @@
}, },
"dom-matches": { "dom-matches": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/dom-matches/download/dom-matches-2.0.0.tgz", "resolved": "https://registry.npmjs.org/dom-matches/-/dom-matches-2.0.0.tgz",
"integrity": "sha1-0nKLQWqHUzmA6wibhI0lPPI6dYw=" "integrity": "sha1-0nKLQWqHUzmA6wibhI0lPPI6dYw="
}, },
"dom-scroll-into-view": { "dom-scroll-into-view": {
@ -7844,6 +7844,11 @@
"resolved": "https://registry.npmjs.org/filepond/-/filepond-4.25.1.tgz", "resolved": "https://registry.npmjs.org/filepond/-/filepond-4.25.1.tgz",
"integrity": "sha512-8UPPTzfpHZRUtJl3dEcGMLYOygg8taJZpYTv1WaEkvzeI+X0JX9+agMJ+5Lu1EKtdt9dGq/9Gud+EnNRBaGl1A==" "integrity": "sha512-8UPPTzfpHZRUtJl3dEcGMLYOygg8taJZpYTv1WaEkvzeI+X0JX9+agMJ+5Lu1EKtdt9dGq/9Gud+EnNRBaGl1A=="
}, },
"filepond-plugin-file-validate-type": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/filepond-plugin-file-validate-type/-/filepond-plugin-file-validate-type-1.2.5.tgz",
"integrity": "sha512-SsdYD6N+PDnetrPrBHfl13ZC9jRv5BuGdai5cORfANhuE/N32OwU/IclCNQ4qlpIa88hzqBg9dj6IINkiW+4HA=="
},
"filepond-plugin-image-preview": { "filepond-plugin-image-preview": {
"version": "4.6.4", "version": "4.6.4",
"resolved": "https://registry.npmjs.org/filepond-plugin-image-preview/-/filepond-plugin-image-preview-4.6.4.tgz", "resolved": "https://registry.npmjs.org/filepond-plugin-image-preview/-/filepond-plugin-image-preview-4.6.4.tgz",
@ -9135,7 +9140,7 @@
}, },
"intersperse": { "intersperse": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/intersperse/download/intersperse-1.0.0.tgz", "resolved": "https://registry.npmjs.org/intersperse/-/intersperse-1.0.0.tgz",
"integrity": "sha1-8lYfsc/vn1J3zDNHoiiGtDUaUYE=" "integrity": "sha1-8lYfsc/vn1J3zDNHoiiGtDUaUYE="
}, },
"invariant": { "invariant": {
@ -10444,7 +10449,7 @@
}, },
"json2mq": { "json2mq": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npm.taobao.org/json2mq/download/json2mq-0.2.0.tgz", "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
"integrity": "sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=", "integrity": "sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=",
"requires": { "requires": {
"string-convert": "^0.2.0" "string-convert": "^0.2.0"
@ -14935,7 +14940,7 @@
}, },
"string-convert": { "string-convert": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npm.taobao.org/string-convert/download/string-convert-0.2.1.tgz", "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
"integrity": "sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c=" "integrity": "sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c="
}, },
"string-length": { "string-length": {

View File

@ -20,11 +20,12 @@
"test:unit": "vue-cli-service test:unit" "test:unit": "vue-cli-service test:unit"
}, },
"dependencies": { "dependencies": {
"ant-design-vue": "^1.7.2", "ant-design-vue": "^1.7.3",
"axios": "^0.21.1", "axios": "^0.21.1",
"dayjs": "^1.10.4", "dayjs": "^1.10.4",
"enquire.js": "^2.1.6", "enquire.js": "^2.1.6",
"filepond": "^4.25.1", "filepond": "^4.25.1",
"filepond-plugin-file-validate-type": "^1.2.5",
"filepond-plugin-image-preview": "^4.6.4", "filepond-plugin-image-preview": "^4.6.4",
"flv.js": "^1.5.0", "flv.js": "^1.5.0",
"halo-editor": "^2.8.2", "halo-editor": "^2.8.2",

View File

@ -4,6 +4,8 @@
@click="handleClick" @click="handleClick"
:icon="computedIcon" :icon="computedIcon"
:loading="loading" :loading="loading"
:size="size"
:block="block"
>{{ computedText }}</a-button> >{{ computedText }}</a-button>
</template> </template>
<script> <script>
@ -18,6 +20,14 @@ export default {
type: String, type: String,
default: null, default: null,
}, },
size: {
type: String,
default: 'default',
},
block: {
type: Boolean,
default: false,
},
loading: { loading: {
type: Boolean, type: Boolean,
default: false, default: false,

View File

@ -6,7 +6,7 @@
:name="name" :name="name"
:allow-multiple="multiple" :allow-multiple="multiple"
:allowRevert="false" :allowRevert="false"
:accepted-file-types="accept" :accepted-file-types="accepts"
:maxParallelUploads="maxParallelUploads" :maxParallelUploads="maxParallelUploads"
:allowImagePreview="allowImagePreview" :allowImagePreview="allowImagePreview"
:maxFiles="maxFiles" :maxFiles="maxFiles"
@ -16,6 +16,8 @@
labelFileProcessingError="上传错误" labelFileProcessingError="上传错误"
labelTapToCancel="点击取消" labelTapToCancel="点击取消"
labelTapToRetry="点击重试" labelTapToRetry="点击重试"
labelFileTypeNotAllowed="不支持当前文件格式"
fileValidateTypeLabelExpectedTypes="请选择 {allTypes} 格式的文件"
:files="fileList" :files="fileList"
:server="server" :server="server"
@init="handleFilePondInit" @init="handleFilePondInit"
@ -33,49 +35,52 @@ import 'filepond/dist/filepond.min.css'
// Plugins // Plugins
import FilePondPluginImagePreview from 'filepond-plugin-image-preview' import FilePondPluginImagePreview from 'filepond-plugin-image-preview'
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css' import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css'
import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type'
// Create component and regist plugins // Create component and regist plugins
const FilePond = vueFilePond(FilePondPluginImagePreview) const FilePond = vueFilePond(FilePondPluginImagePreview, FilePondPluginFileValidateType)
export default { export default {
name: 'FilePondUpload', name: 'FilePondUpload',
components: { components: {
FilePond FilePond,
}, },
props: { props: {
name: { name: {
type: String, type: String,
required: false, required: false,
default: 'file' default: 'file',
}, },
filed: { filed: {
type: String, type: String,
required: false, required: false,
default: '' default: '',
}, },
multiple: { multiple: {
type: Boolean, type: Boolean,
required: false, required: false,
default: true default: true,
}, },
accept: { accepts: {
type: String, type: Array,
required: false, required: false,
default: '' default: () => {
return []
},
}, },
label: { label: {
type: String, type: String,
required: false, required: false,
default: '点击选择文件或将文件拖拽到此处' default: '点击选择文件或将文件拖拽到此处',
}, },
uploadHandler: { uploadHandler: {
type: Function, type: Function,
required: true required: true,
}, },
loadOptions: { loadOptions: {
type: Boolean, type: Boolean,
required: false, required: false,
default: true default: true,
} },
}, },
computed: { computed: {
...mapGetters(['options']), ...mapGetters(['options']),
@ -96,7 +101,7 @@ export default {
return this.options.attachment_upload_max_files return this.options.attachment_upload_max_files
} }
return 1 return 1
} },
}, },
data: function() { data: function() {
return { return {
@ -134,11 +139,11 @@ export default {
abort() abort()
this.$log.debug('Upload operation aborted by the user') this.$log.debug('Upload operation aborted by the user')
source.cancel('Upload operation canceled by the user.') source.cancel('Upload operation canceled by the user.')
} },
}
} }
}, },
fileList: [] },
fileList: [],
} }
}, },
methods: { methods: {
@ -147,7 +152,7 @@ export default {
}, },
handleClearFileList() { handleClearFileList() {
this.$refs.pond.removeFiles() this.$refs.pond.removeFiles()
} },
} },
} }
</script> </script>

View File

@ -74,6 +74,20 @@
animation-name: fadeInUp; animation-name: fadeInUp;
} }
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.fadeIn {
animation-name: fadeIn;
}
.animated { .animated {
-webkit-animation-duration: 1s; -webkit-animation-duration: 1s;
animation-duration: 1s; animation-duration: 1s;

View File

@ -10,6 +10,13 @@
padding: 18px 28px 28px 28px; padding: 18px 28px 28px 28px;
box-shadow: -4px 7px 46px 2px rgba(0, 0, 0, 0.1); box-shadow: -4px 7px 46px 2px rgba(0, 0, 0, 0.1);
.tip {
cursor: pointer;
margin-left: 0.5rem;
float: right;
}
}
.halo-logo { .halo-logo {
margin-bottom: 20px; margin-bottom: 20px;
text-align: center; text-align: center;
@ -31,10 +38,3 @@
} }
} }
} }
.tip {
cursor: pointer;
margin-left: 0.5rem;
float: right;
}
}

View File

@ -149,7 +149,7 @@
<FilePondUpload <FilePondUpload
ref="upload" ref="upload"
name="file" name="file"
accept="application/zip" :accepts="['application/zip']"
label="点击选择主题包或将主题包拖拽到此处<br>仅支持 ZIP 格式的文件" label="点击选择主题包或将主题包拖拽到此处<br>仅支持 ZIP 格式的文件"
:uploadHandler="installModal.local.uploadHandler" :uploadHandler="installModal.local.uploadHandler"
@success="handleUploadSucceed" @success="handleUploadSucceed"
@ -223,7 +223,7 @@
<FilePondUpload <FilePondUpload
ref="updateByupload" ref="updateByupload"
name="file" name="file"
accept="application/zip" :accepts="['application/zip']"
label="点击选择主题更新包或将主题更新包拖拽到此处<br>仅支持 ZIP 格式的文件" label="点击选择主题更新包或将主题更新包拖拽到此处<br>仅支持 ZIP 格式的文件"
:uploadHandler="localUpdateModel.uploadHandler" :uploadHandler="localUpdateModel.uploadHandler"
:filed="localUpdateModel.selected.id" :filed="localUpdateModel.selected.id"

View File

@ -7,41 +7,56 @@
class="h-screen" class="h-screen"
> >
<a-col <a-col
:xl="8" :xxl="8"
:md="12" :xl="12"
:lg="16"
:md="20"
:sm="20" :sm="20"
:xs="24" :xs="23"
> >
<div class="card-container"> <div class="card-container animated fadeIn">
<a-card <a-card
:bordered="false" :bordered="false"
title="Halo 安装向导" style="box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;"
style="box-shadow: 0px 10px 20px 0px rgba(236, 236, 236, 0.86);"
> >
<div class="halo-logo">
<a-steps :current="stepCurrent"> <span>Halo
<a-step title="博主信息"> <small>安装向导</small>
</a-step> </span>
<a-step title="博客信息"> </div>
</a-step> <a-alert
<a-step title="数据导入"> :message="`欢迎使用 Halo您正在安装的是 Halo ${VERSION}。`"
</a-step> type="success"
</a-steps> show-icon
<a-divider dashed /> />
<!-- Blogger info --> <!-- Blogger info -->
<div class="mt-5 mb-5">
<a-radio-group v-model="installationMode">
<a-radio-button value="new">
全新安装
</a-radio-button>
<a-radio-button value="import">
数据导入
</a-radio-button>
</a-radio-group>
</div>
<a-form-model <a-form-model
ref="generalForm" class="installationForm animated fadeIn"
:model="installation" ref="installationForm"
:rules="generalRules" :model="form.model"
:rules="form.rules"
layout="horizontal" layout="horizontal"
v-show="stepCurrent == 0" v-show="isInstallMode"
> >
<a-form-model-item <a-divider
class="animated fadeInUp" orientation="left"
prop="username" dashed
> >
管理员信息
</a-divider>
<a-form-model-item prop="username">
<a-input <a-input
v-model="installation.username" v-model="form.model.username"
placeholder="用户名" placeholder="用户名"
> >
<a-icon <a-icon
@ -51,29 +66,21 @@
/> />
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item prop="username">
class="animated fadeInUp"
:style="{'animation-delay': '0.1s'}"
prop="nickname"
>
<a-input <a-input
v-model="installation.nickname" v-model="form.model.nickname"
placeholder="用户昵称" placeholder="用户昵称"
> >
<a-icon <a-icon
slot="prefix" slot="prefix"
type="smile" type="user"
style="color: rgba(0,0,0,.25)" style="color: rgba(0,0,0,.25)"
/> />
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item prop="email">
class="animated fadeInUp"
:style="{'animation-delay': '0.2s'}"
prop="email"
>
<a-input <a-input
v-model="installation.email" v-model="form.model.email"
placeholder="用户邮箱" placeholder="用户邮箱"
> >
<a-icon <a-icon
@ -83,15 +90,11 @@
/> />
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item prop="password">
class="animated fadeInUp"
:style="{'animation-delay': '0.3s'}"
prop="password"
>
<a-input <a-input
v-model="installation.password" v-model="form.model.password"
type="password" type="password"
placeholder="用户密码8-100位" placeholder="登录密码8-100位"
> >
<a-icon <a-icon
slot="prefix" slot="prefix"
@ -100,15 +103,11 @@
/> />
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item prop="confirmPassword">
class="animated fadeInUp"
:style="{'animation-delay': '0.4s'}"
prop="confirmPassword"
>
<a-input <a-input
v-model="installation.confirmPassword" v-model="form.model.confirmPassword"
type="password" type="password"
placeholder="确认密码" placeholder="确认登录密码"
> >
<a-icon <a-icon
slot="prefix" slot="prefix"
@ -117,23 +116,15 @@
/> />
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
</a-form-model> <a-divider
orientation="left"
<!-- Blog info --> dashed
<a-form-model
layout="horizontal"
v-show="stepCurrent == 1"
ref="blogForm"
:model="installation"
:rules="blogRules"
>
<a-form-model-item
class="animated fadeInUp"
prop="url"
> >
站点信息
</a-divider>
<a-form-model-item prop="url">
<a-input <a-input
v-model="installation.url" v-model="form.model.url"
placeholder="博客地址" placeholder="博客地址"
> >
<a-icon <a-icon
@ -143,13 +134,9 @@
/> />
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>
<a-form-model-item <a-form-model-item prop="title">
class="animated fadeInUp"
:style="{'animation-delay': '0.2s'}"
prop="title"
>
<a-input <a-input
v-model="installation.title" v-model="form.model.title"
placeholder="博客标题" placeholder="博客标题"
> >
<a-icon <a-icon
@ -162,50 +149,51 @@
</a-form-model> </a-form-model>
<!-- Data migration --> <!-- Data migration -->
<div v-show="stepCurrent == 2"> <div
<a-alert class="animated fadeIn"
style="margin-bottom: 1rem" v-show="isImportMode"
message="如果有数据导入需求,请点击并选择之前导出的文件。需要注意的是,并不是所有数据都会导入,该初始化表单的数据会覆盖你导入的数据。" >
type="info"
/>
<FilePondUpload <FilePondUpload
ref="upload" ref="upload"
name="file" name="file"
accept="application/json" :accepts="['application/json']"
label="拖拽或点击选择数据文件请确认是否为 Halo 后台导出的文件。" label="拖拽或点击选择数据文件<br>请确认是否为 Halo 后台导出的文件。"
:multiple="false" :multiple="false"
:uploadHandler="handleMigrationUpload" :uploadHandler="onImportUpload"
:loadOptions="false" :loadOptions="false"
></FilePondUpload> ></FilePondUpload>
</div> </div>
<a-row <div class="mt-8">
class="install-action" <ReactiveButton
type="flex" v-if="isInstallMode"
justify="space-between" icon="check"
style="margin-top: 1rem;"
>
<div>
<a-button
class="previus-button"
v-if="stepCurrent != 0"
@click="stepCurrent--"
style="margin-right: 1rem;"
>上一步</a-button>
<a-button
type="primary" type="primary"
v-if="stepCurrent != 2" block
@click="handleNextStep" size="large"
>下一步</a-button>
</div>
<a-button
v-if="stepCurrent == 2"
type="primary"
icon="upload"
@click="handleInstall" @click="handleInstall"
:loading="installing" @callback="handleInstallCallback"
>安装</a-button> :loading="form.installing"
</a-row> :errored="form.installErrored"
text="安装"
loadedText="安装成功"
erroredText="安装失败"
></ReactiveButton>
<ReactiveButton
v-if="isImportMode"
icon="import"
type="primary"
block
size="large"
@click="handleImport"
@callback="handleImportCallback"
:loading="form.importing"
:errored="form.importErrored"
text="导入"
loadedText="导入成功"
erroredText="导入失败"
></ReactiveButton>
</div>
</a-card> </a-card>
</div> </div>
</a-col> </a-col>
@ -221,26 +209,24 @@ import { mapActions } from 'vuex'
export default { export default {
data() { data() {
const confirmPasswordValidate = (rule, value, callback) => { const confirmPasswordValidate = (rule, value, callback) => {
if (value !== this.installation.password) { if (value !== this.form.model.password) {
callback(new Error('确认密码与所输入的密码不一致')) callback(new Error('确认密码与所输入的密码不一致'))
} else { } else {
callback() callback()
} }
} }
return { return {
installation: {}, installationMode: 'new', // new or import
stepCurrent: 0, form: {
migrationData: null, model: {},
installing: false, rules: {
generalRules: {
username: [ username: [
{ required: true, message: '* 用户名不能为空', trigger: ['change'] }, { required: true, message: '* 用户名不能为空', trigger: ['change'] },
{ max: 50, message: '* 用户名的字符长度不能超过 50', trigger: ['change'] } { max: 50, message: '* 用户名的字符长度不能超过 50', trigger: ['change'] },
], ],
nickname: [ nickname: [
{ required: true, message: '* 用户昵称不能为空', trigger: ['change'] }, { required: true, message: '* 用户昵称不能为空', trigger: ['change'] },
{ max: 255, message: '* 用户昵称的字符长度不能超过 255', trigger: ['change'] } { max: 255, message: '* 用户昵称的字符长度不能超过 255', trigger: ['change'] },
], ],
email: [ email: [
{ required: true, message: '* 电子邮件地址不能为空', trigger: ['change'] }, { required: true, message: '* 电子邮件地址不能为空', trigger: ['change'] },
@ -248,97 +234,124 @@ export default {
{ {
pattern: /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/g, pattern: /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/g,
message: '* 电子邮件地址的格式不正确', message: '* 电子邮件地址的格式不正确',
trigger: ['change'] trigger: ['change'],
} },
], ],
password: [ password: [
{ required: true, message: '* 密码不能为空', trigger: ['change'] }, { required: true, message: '* 密码不能为空', trigger: ['change'] },
{ min: 8, max: 100, message: '* 密码的字符长度必须在 8 - 100 之间', trigger: ['change'] } { min: 8, max: 100, message: '* 密码的字符长度必须在 8 - 100 之间', trigger: ['change'] },
], ],
confirmPassword: [ confirmPassword: [
{ required: true, message: '* 确认密码不能为空', trigger: ['change'] }, { required: true, message: '* 确认密码不能为空', trigger: ['change'] },
{ validator: confirmPasswordValidate, trigger: ['change'] } { validator: confirmPasswordValidate, trigger: ['change'] },
] ],
},
blogRules: {
url: [{ required: true, message: '* 博客地址不能为空', trigger: ['change'] }], url: [{ required: true, message: '* 博客地址不能为空', trigger: ['change'] }],
title: [{ required: true, message: '* 博客标题不能为空', trigger: ['change'] }] title: [{ required: true, message: '* 博客标题不能为空', trigger: ['change'] }],
} },
installing: false,
installErrored: false,
importing: false,
importErrored: false,
importData: null,
},
} }
}, },
beforeMount() { beforeMount() {
this.handleVerifyIsInstall() this.handleVerifyIsInstall()
this.$set(this.installation, 'url', window.location.protocol + '//' + window.location.host) this.$set(this.form.model, 'url', window.location.protocol + '//' + window.location.host)
},
computed: {
isInstallMode() {
return this.installationMode === 'new'
},
isImportMode() {
return this.installationMode === 'import'
},
}, },
methods: { methods: {
...mapActions(['installCleanToken']), ...mapActions(['installCleanToken']),
async handleVerifyIsInstall() { async handleVerifyIsInstall() {
await adminApi.isInstalled().then((response) => { const response = await adminApi.isInstalled()
if (response.data.data) { if (response.data.data) {
this.$router.push({ name: 'Login' }) this.$router.push({ name: 'Login' })
} }
},
handleInstall() {
this.$refs.installationForm.validate((valid) => {
if (valid) {
this.form.installing = true
this.installCleanToken(this.form.model)
.then((response) => {
this.$log.debug('Installation response', response)
})
.catch(() => {
this.form.installErrored = true
})
.finally(() => {
setTimeout(() => {
this.form.installing = false
}, 400)
})
}
}) })
}, },
handleNextStep() { handleInstallCallback() {
if (this.stepCurrent === 0) { if (this.form.installErrored) {
this.$refs.generalForm.validate((valid) => { this.form.installErrored = false
if (valid) {
this.stepCurrent++
} else { } else {
return false this.$message.success('安装成功!')
} this.$router.push({ name: 'Login' })
})
} else if (this.stepCurrent === 1) {
this.$refs.blogForm.validate((valid) => {
if (valid) {
this.stepCurrent++
} else {
return false
}
})
} }
}, },
handleMigrationUpload(data) { onImportUpload(data) {
this.$log.debug('Selected data', data) this.$log.debug('Selected data', data)
this.migrationData = data this.form.importData = data
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.$log.debug('Handle uploading') this.$log.debug('Handle uploading')
resolve() resolve()
}) })
}, },
install() { handleImport() {
this.installCleanToken(this.installation) if (!this.form.importData) {
this.$message.warning('请先上传数据文件!')
return
}
this.form.importing = true
migrateApi
.migrate(this.form.importData)
.then((response) => { .then((response) => {
this.$log.debug('Installation response', response) this.$log.debug('Migrated successfullly')
this.$message.success('安装成功!') })
setTimeout(() => { .catch(() => {
this.$router.push({ name: 'Login' }) this.form.importErrored = true
}, 200)
}) })
.finally(() => { .finally(() => {
setTimeout(() => { setTimeout(() => {
this.installing = false this.form.importing = false
}, 400) }, 400)
}) })
}, },
handleInstall() { handleImportCallback() {
this.installing = true if (this.form.importErrored) {
if (this.migrationData) { this.form.importErrored = false
const hide = this.$message.loading('数据导入中...', 0)
migrateApi
.migrate(this.migrationData)
.then((response) => {
this.$log.debug('Migrated successfullly')
this.$message.success('数据导入成功!')
this.install()
})
.finally(() => {
hide()
})
} else { } else {
this.install() this.$message.success('导入成功!')
} this.$router.push({ name: 'Login' })
}
} }
},
},
} }
</script> </script>
<style lang="less" scoped>
::v-deep .installationForm {
.ant-divider {
.ant-divider-inner-text {
padding-left: 0;
}
&::before {
width: 0;
}
}
}
</style>

View File

@ -103,7 +103,7 @@
<FilePondUpload <FilePondUpload
ref="upload" ref="upload"
name="file" name="file"
accept="text/markdown" :accepts="['text/markdown']"
label="拖拽或点击选择 Markdown 文件到此处" label="拖拽或点击选择 Markdown 文件到此处"
:uploadHandler="uploadHandler" :uploadHandler="uploadHandler"
></FilePondUpload> ></FilePondUpload>

View File

@ -97,19 +97,19 @@ import { mapActions, mapGetters, mapMutations } from 'vuex'
import LoginForm from '@/components/Login/LoginForm' import LoginForm from '@/components/Login/LoginForm'
export default { export default {
components: { components: {
LoginForm LoginForm,
}, },
data() { data() {
return { return {
resetPasswordButtonVisible: false, resetPasswordButtonVisible: false,
apiForm: { apiForm: {
apiUrl: window.location.host, apiUrl: window.location.host,
visible: false visible: false,
} },
} }
}, },
computed: { computed: {
...mapGetters({ defaultApiUrl: 'apiUrl' }) ...mapGetters({ defaultApiUrl: 'apiUrl' }),
}, },
beforeMount() { beforeMount() {
const _this = this const _this = this
@ -124,14 +124,13 @@ export default {
...mapActions(['refreshUserCache', 'refreshOptionsCache']), ...mapActions(['refreshUserCache', 'refreshOptionsCache']),
...mapMutations({ ...mapMutations({
setApiUrl: 'SET_API_URL', setApiUrl: 'SET_API_URL',
restoreApiUrl: 'RESTORE_API_URL' restoreApiUrl: 'RESTORE_API_URL',
}), }),
handleVerifyIsInstall() { async handleVerifyIsInstall() {
adminApi.isInstalled().then((response) => { const response = await adminApi.isInstalled()
if (!response.data.data) { if (!response.data.data) {
this.$router.push({ name: 'Install' }) this.$router.push({ name: 'Install' })
} }
})
}, },
onLoginSucceed() { onLoginSucceed() {
// Refresh the user info // Refresh the user info
@ -156,7 +155,7 @@ export default {
if (this.apiForm.visible) { if (this.apiForm.visible) {
this.apiForm.apiUrl = this.defaultApiUrl this.apiForm.apiUrl = this.defaultApiUrl
} }
} },
} },
} }
</script> </script>