add splash; refactor some key steps from dialog to page

This commit is contained in:
geekgeekrun
2024-03-08 09:41:49 +08:00
parent 75924a4747
commit fc68291923
9 changed files with 396 additions and 28 deletions

View File

@@ -1,6 +1,8 @@
<template>
<el-config-provider :locale="zhCn">
<RouterView />
<Suspense>
<RouterView />
</Suspense>
</el-config-provider>
</template>

View File

@@ -0,0 +1,38 @@
<template>
<div>
<div>愿你心想事成</div>
<RouterView
:dependencies-status="checkDependenciesResult"
:process-waitee="downloadProcessWaitee"
></RouterView>
</div>
</template>
<script lang="ts" setup>
import { useRouter } from 'vue-router'
import { onMounted } from 'vue'
import { sleep } from '@geekgeekrun/utils/sleep.mjs'
const router = useRouter()
onMounted(async () => {
const checkDependenciesResult = await electron.ipcRenderer.invoke('check-dependencies')
const downloadProcessWaitee = Promise.withResolvers()
if (Object.values(checkDependenciesResult).includes(false)) {
router.replace('/downloadingDependencies')
} else {
downloadProcessWaitee.resolve()
}
downloadProcessWaitee.promise.then(async () => {
const isCookieFileValid = await electron.ipcRenderer.invoke('check-boss-zhipin-cookie-file')
if (!isCookieFileValid) {
router.replace('/cookieAssistant')
} else {
await sleep(1000)
router.replace('/configuration')
}
})
})
</script>

View File

@@ -0,0 +1,85 @@
<template>
<div v-if="!dependenciesStatus.puppeteerExecutableAvailable">
<div mb14px>正在下载核心组件</div>
<el-progress
:percentage="browserDownloadPercentage"
:format="(n) => `${n.toFixed(1)}%`"
:stroke-width="10"
/>
</div>
</template>
<script lang="ts" setup>
import { ref, onUnmounted, PropType } from 'vue'
import { ElMessageBox } from 'element-plus'
const props = defineProps({
dependenciesStatus: {
type: Object as PropType<Record<string, boolean>>,
default: () => ({})
},
processWaitee: Object
})
const browserDownloadPercentage = ref(0)
const handleBrowserDownloadProgress = (ev, { downloadedBytes, totalBytes }) => {
browserDownloadPercentage.value = (downloadedBytes / totalBytes) * 100
}
electron.ipcRenderer.on('PUPPETEER_DOWNLOAD_PROGRESS', handleBrowserDownloadProgress)
onUnmounted(() =>
electron.ipcRenderer.removeListener('PUPPETEER_DOWNLOAD_PROGRESS', handleBrowserDownloadProgress)
)
const downloadProcessExitCode = ref(0)
const processDownloadBrowser = async () => {
downloadProcessExitCode.value = 0
browserDownloadPercentage.value = 0
try {
await electron.ipcRenderer.invoke('setup-dependencies')
browserDownloadPercentage.value = 100
} catch (err) {
downloadProcessExitCode.value = 1
throw err
}
}
const promiseList: Array<Promise<void>> = []
const processTasks = async () => {
if (!props.dependenciesStatus.puppeteerExecutableAvailable) {
const p = processDownloadBrowser()
promiseList.push(p)
p.then(() => {
props.dependenciesStatus.puppeteerExecutableAvailable = true
})
}
while (promiseList.length) {
const p = promiseList.shift()!
try {
p.then(() => {
if (!promiseList.length) {
props.processWaitee?.resolve?.()
}
})
await p
} catch {
await ElMessageBox.confirm('需要重试吗?', '核心组件下载失败', {
closeOnClickModal: false,
closeOnPressEscape: false,
showClose: false,
type: 'error',
cancelButtonText: '退出程序'
})
.then(() => {
processTasks()
})
.catch(() => {
// FIXME: should exit app here
promiseList.length = 0
})
}
}
}
processTasks()
</script>

View File

@@ -28,9 +28,8 @@
<script setup lang="ts">
import { ref } from 'vue'
import { ElForm, ElMessage } from 'element-plus'
import router from '../../router/index'
import { mountGlobalDialog as mountDependenciesSetupProgressIndicatorDialog } from '@renderer/features/DependenciesSetupProgressIndicatorDialog/operations'
import { mountGlobalDialog as mountWaitForLoginDialog } from '@renderer/features/WaitForLoginDialog/operations'
import { useRouter } from 'vue-router'
const router = useRouter()
const formContent = ref({
dingtalkRobotAccessToken: '',
@@ -67,7 +66,7 @@ const handleSubmit = async () => {
})
const checkDependenciesResult = await electron.ipcRenderer.invoke('check-dependencies')
if (Object.values(checkDependenciesResult).includes(false)) {
mountDependenciesSetupProgressIndicatorDialog(checkDependenciesResult)
router.replace('/')
// TODO: should continue interrupted task
}
}
@@ -89,7 +88,7 @@ const handleExpectCompaniesInputBlur = (event) => {
}
const handleClickLaunchLogin = () => {
mountWaitForLoginDialog()
router.replace('/cookieAssistant')
}
</script>

View File

@@ -2,9 +2,8 @@
<script lang="ts" setup>
import { onMounted, onUnmounted } from 'vue'
import { mountGlobalDialog as mountDependenciesSetupProgressIndicatorDialog } from '@renderer/features/DependenciesSetupProgressIndicatorDialog/operations'
import { mountGlobalDialog as mountWaitForLoginDialog } from '@renderer/features/WaitForLoginDialog/operations'
import { useRouter } from 'vue-router'
const router = useRouter()
const unmountedCbs: Array<InstanceType<typeof Function>> = []
onUnmounted(() => {
while (unmountedCbs.length) {
@@ -14,31 +13,27 @@ onUnmounted(() => {
} catch {}
}
})
const goToCheckBossZhipinCookieFile = () => router.replace('/cookieAssistant')
onMounted(() => {
electron.ipcRenderer.on('check-boss-zhipin-cookie-file', mountWaitForLoginDialog)
electron.ipcRenderer.on('check-boss-zhipin-cookie-file', goToCheckBossZhipinCookieFile)
})
onUnmounted(() => {
electron.ipcRenderer.removeListener('check-boss-zhipin-cookie-file', mountWaitForLoginDialog)
electron.ipcRenderer.removeListener(
'check-boss-zhipin-cookie-file',
goToCheckBossZhipinCookieFile
)
})
;(async () => {
const checkDependenciesResult = await electron.ipcRenderer.invoke('check-dependencies')
if (Object.values(checkDependenciesResult).includes(false)) {
const processWaitee = Promise.withResolvers()
mountDependenciesSetupProgressIndicatorDialog({
checkDependenciesResult, processWaitee
})
await processWaitee.promise
router.replace('/')
return
}
const isCookieFileValid = await electron.ipcRenderer.invoke('check-boss-zhipin-cookie-file')
if (!isCookieFileValid) {
const processWaitee = Promise.withResolvers()
mountWaitForLoginDialog({
processWaitee
})
await processWaitee.promise
router.replace('/cookieAssistant')
return
}
})()
</script>

View File

@@ -0,0 +1,236 @@
<template>
<div>
<h1>Cookie 配置助手</h1>
<el-alert
v-if="cookieInvalid"
type="warning"
:closable="false"
title="需要获取您的Boss直聘Cookie才能继续"
>
由于您是首次使用本程序或者您之前使用的Boss直聘账号登录状态失效因此您需要重新获取登录凭证
</el-alert>
<div ml1em mt1em line-height-normal>
如果您了解如何获取Cookie了解有效的Cookie格式可以直接在下方输入框中进行编辑<br />
手动编辑较为麻烦建议您打开已登录过Boss直聘的浏览器使用
<a
color-blue
decoration-none
href="javascript:void(0)"
@click.prevent="handleEditThisCookieExtensionStoreLinkClick"
>EditThisCookie 扩展程序</a
>
复制Cookie然后粘贴在下方输入框中<br />
格式为被序列化为JSON的数组不含两侧引号
</div>
<br />
<div ml1em line-height-normal>
如果您不了解Cookie相关概念或者不能访问Chrome扩展程序商店下载EditThisCookie来获取Cookie请按照以下步骤进行操作
</div>
<ol lh-2em mt-0>
<li>
<el-button size="small" type="primary" font-size-inherit @click="handleClickLaunchLogin"
>点击此处</el-button
>
启动浏览器
</li>
<li>按照正常流程通过 <b>短信验证码/二维码/微信小程序</b> 登录您的Boss直聘账号</li>
<li>接下来将自动进行一些页面跳转最终将会停留在首页</li>
<li>
登录后预计5-10秒内具体取决于您的网速您的Cookie将被自动填入下方输入框
<details>
<summary color-orange cursor-pointer>我已完成登录但Cookie一直没出现</summary>
<div ml-2em max-h-200px of-auto>
如果您确实已经在打开浏览器中看到您已登录了Boss直聘请尝试按照如图所示方式复制Cookie
<figure>
<figcaption>依次点击浏览器右上角扩展程序图标EditThisCookie图标</figcaption>
<img block max-w-full src="./resources/copy-cookie-step-1.png" />
</figure>
<figure>
<figcaption>点击EditThisCookie弹出框中的Export按钮</figcaption>
<img block max-w-full src="./resources/copy-cookie-step-2.png" />
</figure>
<figure>
<figcaption>在下方输入框执行粘贴操作</figcaption>
</figure>
</div>
</details>
</li>
</ol>
<el-form
ref="formRef"
inline-message
:model="formContent"
label-position="top"
:rules="formRules"
class="cookie-form"
>
<el-form-item prop="collectedCookies" mb-0>
<el-input
v-model="formContent.collectedCookies"
type="textarea"
:autosize="{
minRows: 10,
maxRows: 10
}"
font-size-12px
@input="hasUserMutateInput = true"
></el-input>
<el-alert
v-if="loginCookieWaitingStatus === LOGIN_COOKIE_WAITING_STATUS.WAITING_FOR_LOGIN"
:closable="false"
>正在等待登录</el-alert
>
<el-alert
v-if="loginCookieWaitingStatus === LOGIN_COOKIE_WAITING_STATUS.COOKIE_COLLECTED"
:closable="false"
type="success"
>已获取到Cookie<template v-if="hasUserMutateInput"
>看起来您似乎正在尝试手动输入Cookie<el-button
size="small"
type="primary"
font-size-inherit
@click="fillCollectedCookie"
>使用获取到的Cookie</el-button
></template
></el-alert
>
</el-form-item>
</el-form>
<footer flex mt20px pb20px flex-justify-end>
<el-button v-if="!cookieInvalid" @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</footer>
</div>
</template>
<script lang="ts" setup>
import { ElForm, ElMessage } from 'element-plus'
import { ref, onUnmounted, onMounted } from 'vue'
import { checkCookieListFormat } from '../../../../common/utils/cookie'
import { useRouter } from 'vue-router';
const router = useRouter()
const cookieInvalid = ref(false)
enum LOGIN_COOKIE_WAITING_STATUS {
INIT,
WAITING_FOR_LOGIN,
COOKIE_COLLECTED
}
const loginCookieWaitingStatus = ref(LOGIN_COOKIE_WAITING_STATUS.INIT)
const formRef = ref<InstanceType<typeof ElForm>>()
const formContent = ref({
collectedCookies: ''
})
const formRules = {
collectedCookies: [
{
required: true,
message: '请输入Cookie'
},
{
trigger: 'blur',
validator(rule, val, cb) {
let arr
try {
arr = JSON.parse(val)
} catch (err) {
cb(
new Error(
`JSON格式无效 - 存在语法错误: ${err.message}建议使用EditThisCookie扩展程序进行复制。`
)
)
return
}
if (!checkCookieListFormat(JSON.parse(formContent.value.collectedCookies))) {
cb(new Error(`Cookie格式无效 - 部分字段缺失建议使用EditThisCookie扩展程序进行复制。`))
return
}
cb()
}
}
]
}
const hasUserMutateInput = ref(false)
const collectedCookie = ref()
const handleCookieCollected = (_, payload) => {
loginCookieWaitingStatus.value = LOGIN_COOKIE_WAITING_STATUS.COOKIE_COLLECTED
collectedCookie.value = payload.cookies
if (!hasUserMutateInput.value) {
fillCollectedCookie()
}
}
const fillCollectedCookie = () => {
if (loginCookieWaitingStatus.value !== LOGIN_COOKIE_WAITING_STATUS.COOKIE_COLLECTED) {
return
}
formContent.value.collectedCookies = JSON.stringify(collectedCookie.value, null, 2)
hasUserMutateInput.value = false
}
const handleClickLaunchLogin = () => {
electron.ipcRenderer.send('launch-bosszhipin-login-page-with-preload-extension')
loginCookieWaitingStatus.value = LOGIN_COOKIE_WAITING_STATUS.WAITING_FOR_LOGIN
}
const handleEditThisCookieExtensionStoreLinkClick = () => {
electron.ipcRenderer.send(
'open-external-link',
'https://chromewebstore.google.com/detail/editthiscookie/fngmhnnpilhplaeedifhccceomclgfbg'
)
}
const handleCancel = () => {
router.replace('/configuration')
}
const handleSubmit = async () => {
await formRef.value!.validate()
await electron.ipcRenderer.invoke('write-storage-file', {
fileName: 'boss-cookies.json',
data: formContent.value.collectedCookies
})
ElMessage.success('Boss直聘 Cookie 保存成功')
router.replace('/configuration')
}
const handleBossZhipinLoginPageClosed = () => {
if (loginCookieWaitingStatus.value === LOGIN_COOKIE_WAITING_STATUS.WAITING_FOR_LOGIN) {
loginCookieWaitingStatus.value = LOGIN_COOKIE_WAITING_STATUS.INIT
}
}
onMounted(async () => {
electron.ipcRenderer.once('BOSS_ZHIPIN_COOKIE_COLLECTED', handleCookieCollected)
electron.ipcRenderer.on('BOSS_ZHIPIN_LOGIN_PAGE_CLOSED', handleBossZhipinLoginPageClosed)
const cookieFileContent = await electron.ipcRenderer.invoke('read-storage-file', {
fileName: 'boss-cookies.json'
})
if (checkCookieListFormat(cookieFileContent)) {
formContent.value.collectedCookies = JSON.stringify(cookieFileContent, null, 2)
} else {
cookieInvalid.value = true
}
})
onUnmounted(() => {
electron.ipcRenderer.removeListener('BOSS_ZHIPIN_COOKIE_COLLECTED', handleCookieCollected)
electron.ipcRenderer.removeListener(
'BOSS_ZHIPIN_LOGIN_PAGE_CLOSED',
handleBossZhipinLoginPageClosed
)
electron.ipcRenderer.send('kill-bosszhipin-login-page-with-preload-extension')
})
</script>
<style lang="scss">
.cookie-form.el-form {
.el-form-item__error--inline {
margin-left: 0;
margin-top: 10px;
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

@@ -1,12 +1,11 @@
import { defineComponent, h } from 'vue'
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import BootstrapSplash from '@renderer/page/BootstrapSplash/index.vue'
const routes: Array<RouteRecordRaw> = [
{
path: '/',
component: defineComponent({ setup: () => h('div') }),
redirect: '/configuration',
children: []
path: '/cookieAssistant',
component: () => import('@renderer/page/CookieAssistant/index.vue')
},
{
path: '/configuration',
@@ -34,7 +33,21 @@ const routes: Array<RouteRecordRaw> = [
}
}
]
}
},
{
path: '/',
component: BootstrapSplash,
children: [
// {
// path: '/',
// component: () => defineComponent({ setup: () => () => h('div') })
// },
{
path: '/downloadingDependencies',
component: () => import('@renderer/page/BootstrapSplash/page/DownloadingDependencies.vue')
}
]
},
]
const router = createRouter({