1 Commits

Author SHA1 Message Date
451add0e7d feat: update 2025-06-02 23:58:06 +08:00
191 changed files with 14324 additions and 13235 deletions

1
.gitignore vendored
View File

@ -1,5 +1,4 @@
dist/ dist/
dist-dev/
deploy_versions/ deploy_versions/
.temp/ .temp/
.rn_temp/ .rn_temp/

View File

@ -12,20 +12,6 @@ const MockPlugin: PluginItem = [
} }
]; ];
const CIPluginOpt = () => {
const brand = process.env.BRAND as 'prod' | 'dev'; // 取环境变量
const map = {
prod: { appid: 'wxf0724a83f8e377d2', privateKeyPath: 'keys/private.wxf0724a83f8e377d2.key' },
dev: { appid: 'wx2d56597fd541f1a6', privateKeyPath: 'keys/private.wx2d56597fd541f1a6.key' },
};
return {
weapp: map[brand] || map.prod,
version: process.env.VERSION || '1.0.0',
desc: `build for ${brand}`,
};
};
// https://taro-docs.jd.com/docs/next/config#defineconfig-辅助函数 // https://taro-docs.jd.com/docs/next/config#defineconfig-辅助函数
export default defineConfig(async (merge, _env: ConfigEnv) => { export default defineConfig(async (merge, _env: ConfigEnv) => {
const baseConfig: UserConfigExport = { const baseConfig: UserConfigExport = {
@ -39,15 +25,13 @@ export default defineConfig(async (merge, _env: ConfigEnv) => {
828: 1.81 / 2 828: 1.81 / 2
}, },
sourceRoot: 'src', sourceRoot: 'src',
outputRoot: process.env.OUTPUT_ROOT || 'dist', outputRoot: 'dist',
plugins: [['@tarojs/plugin-mini-ci', CIPluginOpt()], '@tarojs/plugin-html'], plugins: ['@tarojs/plugin-html'],
// plugins: ['@tarojs/plugin-html', MockPlugin], // plugins: ['@tarojs/plugin-html', MockPlugin],
defineConstants: { defineConstants: {
'process.env.BRAND': JSON.stringify(process.env.BRAND),
}, },
copy: { copy: {
patterns: [ patterns: [
{from: 'project.config.dev.json',to: 'dist-dev/project.config.json'},
], ],
options: { options: {
} }

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEArjidDs/HGidBTzyOssyhX/Yvlr+BZxO3Qqe1iFBIJYWVXwiJ
KFVZw14rPKnh6ORB9qpMq3+fYJ7VsodmobnVlTaFXxJ8yi+tVZSZyZPtHabu2JVO
qu7LX709p5NTFcrmOY6+NJA827+hg6V0Nl3b4CWLsouPHgiIxpqnLeaOka3lbPMq
vQuDeMCbQNFe5Jz7C0gc6dXwEpDExNX4WiVfi4UP5bcYiUKlbdben5i6qbFrZ4QK
woYoAIhr60jQGfB6Jg7YkbbAyh42XR3BMkf2TPQqieZM1/IKNuw2evtA2BqLJuiz
cQ1qNBBXczbJv9KjBVAzAJgT4InUyJF+QfFJ6wIDAQABAoIBAQCf2kohIPvqP0Jp
VYMJA0C+L8CSbLxqu2hWvFKP9Dl+VAct30MZwtZNr8W0L5uceHMdxDv8z2iI2rPw
ztOYaE5UTmYpSWk+imhcMbC5iAT0RAq6pqQ7qkboXVaIv2BoinabMSHMUNvGNRoZ
FGKAz7XDADAhbnOudt4ofBYQw47kkko8vaDZBJS6UGN+QVJpyBtJKohaXKC6HRva
2c8zDwVZlKMrPl4WB4cg0+WPv6vUZymv8U7flwY0xJBBWwZUxqbmjqbTg8BGb5Mu
tpIZRvTGNxbDu9Ms7mdYdGGe/PFmqgj1hOomeXvBlnYz+fmWHu5ElyPGBHD92N7x
Hdcopp4BAoGBANqQUfxzjGcBvSSSowehSvgVCnUFsTxSE8c9HQRX4LtMnLCo6c6m
Hxf+l67FyY5704EW29PLu5ETdLBm6pCaIk8aJo467l8M9BOHmE/CwNciloHxXa4h
/gUKex6EsFJoT+1gaCaDJeu0gDJl49QnpYOrma2CZ8vyG3+NCotuYturAoGBAMwP
8G2ObLZUFPZAfhGZrobUom2dCtJXenYupxSvJ/rNd7zY+F3pcyi461eMIkKFaxQX
bRyDi/LRCQRKrSDuw3Dt4Ne9gkpdlI/LUDTUUbWdDuCXxfsXSRAYHC1ae0rsiGQv
PaTotkDSjdAIKdgd0bmeufFZ2Ozmid2lHzSZfwrBAoGAQXSVSSClvLeVmJ1du8w/
MnrCrfctdB40lCemSAW1ZQJ1rqZ3lw7DIyF49buAOG63EeLxpMVQAAuul6HKv9GZ
Us1HHLNjbg3oDu093S07KXLPVk0dvCer9oCEDMi6MYPR5ApKRrZiJevhtLY0+/V/
qSY8GO2D/l0chR+NN+KwMo8CgYB4Y07s04G/b4uOXC4EJC6azQems2eXAJT29D8O
LOfhRybE+xxMC1YtSCtP0xTDOgMaL1fn4pPGBRzUs1nAfBxBm5VyshMT3HGxA7ml
8TzjcG0+g0wB/pLUM+UrvJ3QbgtpT5WGnOQpCg8wFCDbp3v7jnEUunUIDlp3sNrg
JU6WAQKBgDjcfRDtnmSxSiejsZaZM7gCAYlZq6WBuvQ6WF+nD9mzEgzkw3F+1cHC
Y905VPGb/vCPAV+JdN7LxnMn2G3JoWPqrhOIT6KgPgAYbVAq1hmLqXMjGi53n9uY
39GL4UgJkywIdEkla/8zNFNKZgQulWPDJqb/MFcVo3/VegEfwdbk
-----END RSA PRIVATE KEY-----

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA1Tl6FYIIWB8b+OjYY9s6cOJf71Vn0J/u/kdq5CH/LMOdNrUh
fq3shAwdDkXmuRIpHU7Noxisu71tB2EumigIwBDRVyrdwPu/p6QWyaMgrh0bdtaC
LKW/j2teNU1MCSWyCDyEeZ9PJteK5+ieX+ZPDg17HLV2dISpB71AGyM461DIHCAV
uaanUJ4MYCtp9t7OFgrD4dNzDMH0lBJBczRQAmnit0GpJD/y2rfgtL+HaKpDN8rc
w80+3HVfhKq3niy/vBbI6alqFb1Mh9O5q6/c1tEWQKPn3TzneGIDhMupGzHEzrVR
j87+1cyYi8PtBdNMPls+zPn7MoajjrASFyo1hQIDAQABAoIBABHu652zehAvTaL5
snlzjs6/u2HdxQV+7hLk3tChK10QdsCLK24EIshYXHk+19Do/hxA7HqzMJP5spnU
Sp8cjt/YikLSkCQTCGOJIYGD/tOmDxFHqVsn9ZySYzIIdkSurvKSZHH2PCoAJuaq
Yeee7zc3WwtnF7dlEPJdsG6X38mqslzaLOuiftkHJ8hQ+1rWM9FFEp/S3pLfLB+7
jEQhuVk0WjLTqmWmKvi0Nz5tUibBpF1OHtCfIhPjb1N8k/bcv1jumETx+2PDxA62
zpPNGespBaET+jeEqNCqLO7Zq8+iUO8d4XpZsFCR2cyE6zzXK2mhQ+2pW2BRU/DI
w/ZylmECgYEA6uPRJ4uk+NavoJlQCv7R9B8CGbwzbRaG8sg/NuwvTOO6D4FGUdcu
f8qA2kCd8bSCuWrS1mVgPpvzwHt5Bh3k7FC/0SfW5ZDC5Lp1IssI0jeka39vfPOD
ZdcvgJ3dUnHUrvb4L4PclgCa3KPXLTVDnK+4ExvaYOxNAaG6wROCGBsCgYEA6GMy
nCsPr21kKmhybCrhcTYoENb45hz6CCnXr4MGnYzF9c2ss2KXjR/boCdhF+3wxmmm
F4TKgcicOUfOJIXSIHBD9nphLa5CfrksLxfX0iIyNCG74aXIWl6M6QgQZdWqV4qC
xYTtFYACELIb8qK/mraMP28c9BzDpwd909ZlAt8CgYEAmpXCIa+CrA3GWgBsMEBR
9sKthDha8/E+cOuexsubuZOp8OhYWiYuwQWJ1ZlvGRJ+cf6+sUDYnXvPaJ8aIA9u
iKQACWuZv02NJjEcRACxVoAuIl0lB2vPzydWaOP8M3UczuVUoxi6BLJzOMBPLb5/
N9BhaZoMF3uMloVUvhrTpH8CgYA9sQfgeXYChjHPKEcn/AI5Qb1FKyyMOikOPb1q
dIgvDw4GgQFV5diBuHFfhNS7u4jtw2I6YCA6b/05H5zUCUqNHgCM0FQ4AkjYx2O5
ZMUAnd9iA4qTDqWvh+Y0ftWLkztTXgv1T6J40LC7qezNbvMaqVd2B5NSIsi18MEw
mXvbVQKBgQC0Rh5TJEfeBWRoIixwFuDUgHYRsdk3SV1hymYO3aKBr0uD8Arv4v1M
XO6DchFjcpKuwScjXX5yEot5PJOUTPG2T2uBPnJggtNig2dnRzQDLM+bK0vVecoz
QL9YMOYv6DNH6Z2yEfMZHZLto9Oaqm9J62hZahZ2GukFLOMBGZAVhA==
-----END RSA PRIVATE KEY-----

View File

@ -10,10 +10,29 @@
"framework": "React" "framework": "React"
}, },
"scripts": { "scripts": {
"build:weapp": "BRAND=prod taro build --type weapp", "build:weapp": "taro build --type weapp",
"build:dev:weapp": "BRAND=dev OUTPUT_ROOT=dist-dev taro build --type weapp --upload", "build:swan": "taro build --type swan",
"dev:weapp": "BRAND=prod npm run build:weapp -- --watch", "build:alipay": "taro build --type alipay",
"dev:dev:weapp": "BRAND=dev OUTPUT_ROOT=dist-dev taro build --type weapp --watch" "build:tt": "taro build --type tt",
"build:h5": "taro build --type h5",
"build:rn": "taro build --type rn",
"build:qq": "taro build --type qq",
"build:jd": "taro build --type jd",
"build:quickapp": "taro build --type quickapp",
"build:harmony-hybrid": "taro build --type harmony-hybrid",
"dev:weapp": "npm run build:weapp -- --watch",
"dev:swan": "npm run build:swan -- --watch",
"dev:alipay": "npm run build:alipay -- --watch",
"dev:tt": "npm run build:tt -- --watch",
"dev:h5": "npm run build:h5 -- --watch",
"dev:rn": "npm run build:rn -- --watch",
"dev:qq": "npm run build:qq -- --watch",
"dev:jd": "npm run build:jd -- --watch",
"dev:quickapp": "npm run build:quickapp -- --watch",
"dev:harmony-hybrid": "npm run build:harmony-hybrid -- --watch",
"test": "jest",
"lint:format": "npx prettier --write",
"lint:format-all": "npx prettier --write 'src/**/*.{ts,js,css,jsx,html,vue,tsx}'"
}, },
"browserslist": [ "browserslist": [
"last 3 versions", "last 3 versions",
@ -57,7 +76,6 @@
"@babel/core": "^7.8.0", "@babel/core": "^7.8.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.5", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.5",
"@tarojs/cli": "4.0.9", "@tarojs/cli": "4.0.9",
"@tarojs/plugin-mini-ci": "^4.1.5",
"@tarojs/plugin-mock": "^0.0.9", "@tarojs/plugin-mock": "^0.0.9",
"@tarojs/taro-loader": "4.0.9", "@tarojs/taro-loader": "4.0.9",
"@tarojs/test-utils-react": "^0.1.1", "@tarojs/test-utils-react": "^0.1.1",
@ -77,7 +95,6 @@
"eslint-plugin-react-hooks": "^4.2.0", "eslint-plugin-react-hooks": "^4.2.0",
"jest": "^29.3.1", "jest": "^29.3.1",
"jest-environment-jsdom": "^29.5.0", "jest-environment-jsdom": "^29.5.0",
"miniprogram-ci": "^2.1.14",
"postcss": "^8.4.18", "postcss": "^8.4.18",
"prettier": "^3.3.1", "prettier": "^3.3.1",
"react-refresh": "^0.11.0", "react-refresh": "^0.11.0",
@ -92,6 +109,5 @@
"ignoredBuiltDependencies": [ "ignoredBuiltDependencies": [
"@tarojs/binding" "@tarojs/binding"
] ]
}, }
"packageManager": "pnpm@10.12.4+sha512.5ea8b0deed94ed68691c9bad4c955492705c5eeb8a87ef86bc62c74a26b037b08ff9570f108b2e4dbd1dd1a9186fea925e527f141c648e85af45631074680184"
} }

6883
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +0,0 @@
{
"miniprogramRoot": "./",
"description": "boluo app",
"setting": {
"urlCheck": true,
"es6": false,
"enhance": false,
"compileHotReLoad": false,
"postcss": false,
"minified": true,
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
},
"coverView": false,
"showShadowRootInWxmlPanel": false,
"packNpmRelationList": [],
"ignoreUploadUnusedFiles": true,
"compileWorklet": false,
"uglifyFileName": false,
"uploadWithSourceMap": true,
"packNpmManually": false,
"minifyWXSS": true,
"minifyWXML": true,
"localPlugins": false,
"disableUseStrict": false,
"useCompilerPlugins": false,
"condition": false,
"swc": false,
"disableSWC": true
},
"compileType": "miniprogram",
"srcMiniprogramRoot": "dist/",
"condition": {},
"editorSetting": {
"tabIndent": "insertSpaces",
"tabSize": 2
},
"libVersion": "3.4.6",
"packOptions": {
"ignore": [],
"include": []
},
"appid": "wx2d56597fd541f1a6",
"simulatorPluginLibVersion": {}
}

View File

@ -16,19 +16,7 @@
"coverView": false, "coverView": false,
"showShadowRootInWxmlPanel": false, "showShadowRootInWxmlPanel": false,
"packNpmRelationList": [], "packNpmRelationList": [],
"ignoreUploadUnusedFiles": true, "ignoreUploadUnusedFiles": true
"compileWorklet": false,
"uglifyFileName": false,
"uploadWithSourceMap": true,
"packNpmManually": false,
"minifyWXSS": true,
"minifyWXML": true,
"localPlugins": false,
"disableUseStrict": false,
"useCompilerPlugins": false,
"condition": false,
"swc": false,
"disableSWC": true
}, },
"compileType": "miniprogram", "compileType": "miniprogram",
"srcMiniprogramRoot": "dist/", "srcMiniprogramRoot": "dist/",
@ -42,6 +30,5 @@
"ignore": [], "ignore": [],
"include": [] "include": []
}, },
"appid": "wxf0724a83f8e377d2", "appid": "wxf0724a83f8e377d2"
"simulatorPluginLibVersion": {}
} }

View File

@ -13,8 +13,3 @@ page {
.base-bg(); .base-bg();
} }
} }
@font-face {
font-family: 'Alimama ShuHeiTi';
src: url('https://publiccdn.neighbourhood.com.cn/font/almmheiti.ttf');
}

View File

@ -1,4 +1,4 @@
import Taro, { useDidShow, useLaunch } from '@tarojs/taro'; import { useLaunch } from '@tarojs/taro';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
@ -6,38 +6,14 @@ import { Provider } from 'react-redux';
import { REFRESH_UNREAD_COUNT_TIME } from '@/constants/message'; import { REFRESH_UNREAD_COUNT_TIME } from '@/constants/message';
import http from '@/http'; import http from '@/http';
import store from '@/store'; import store from '@/store';
import { requestCityConfigs } from '@/utils/location';
import { requestUnreadMessageCount } from '@/utils/message'; import { requestUnreadMessageCount } from '@/utils/message';
import { decryptOpenGid, getInviteCode, getInviteCodeFromQuery } from '@/utils/partner'; import { getInviteCode, getInviteCodeFromQuery } from '@/utils/partner';
import qiniuUpload from '@/utils/qiniu-upload'; import qiniuUpload from '@/utils/qiniu-upload';
import { requestUserInfo, updateLastLoginTime } from '@/utils/user'; import { requestUserInfo, updateLastLoginTime } from '@/utils/user';
import './app.less'; import './app.less';
function App({ children }: PropsWithChildren<BL.Anything>) { function App({ children }: PropsWithChildren<BL.Anything>) {
const checkBasicUpdate = () => {
if (process.env.TARO_ENV !== 'weapp' || !Taro.canIUse('getUpdateManager')) {
return;
}
const manager = Taro.getUpdateManager();
manager.onCheckForUpdate(res => {
console.log('onCheckForUpdate', res);
});
// 新版本下载完成
manager.onUpdateReady(() => {
Taro.showModal({
title: '更新提示',
content: '新版本已经准备好,是否重启应用?',
success: function (res) {
if (res.confirm) {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
manager.applyUpdate();
}
},
});
});
};
useLaunch(async ({ query }) => { useLaunch(async ({ query }) => {
console.log('App launched.'); console.log('App launched.');
await http.init(getInviteCodeFromQuery(query)); await http.init(getInviteCodeFromQuery(query));
@ -52,34 +28,6 @@ function App({ children }: PropsWithChildren<BL.Anything>) {
setInterval(() => requestUnreadMessageCount(), REFRESH_UNREAD_COUNT_TIME); setInterval(() => requestUnreadMessageCount(), REFRESH_UNREAD_COUNT_TIME);
}); });
useDidShow(options => {
checkBasicUpdate();
requestCityConfigs();
console.log(options);
const inviteCode = getInviteCodeFromQuery(options?.query || {});
const authCode = options?.query?.authCode;
if (inviteCode || authCode) {
Taro.getGroupEnterInfo()
.then(info => {
decryptOpenGid({
inviteCode,
authCode,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
iv: info.iv,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
encryptedData: info.encryptedData,
});
console.log('getGroupEnterInfo', info);
})
.catch(() => {
console.log('没有解析到群', options?.scene);
});
}
});
return <Provider store={store}>{children}</Provider>; return <Provider store={store}>{children}</Provider>;
} }

View File

@ -1,41 +0,0 @@
@import '@/styles/common.less';
@import '@/styles/variables.less';
.agreement-popup {
&__content {
.flex-column();
padding: 40px 32px;
}
&__title {
font-size: 32px;
font-weight: 500;
line-height: 48px;
color: @blColor;;
margin-bottom: 24px;
}
&__body {
font-weight: 400;
font-size: 28px;
line-height: 40px;
color: @blColor;
margin-bottom: 24px;
align-self: flex-start;
};
&__btn-group {
margin-top: 32px;
.flex-row();
gap: 30px;
}
&__confirm-button {
.button(@width: 230px, @height: 72px, @fontSize: 28px, @fontWeight: 400, @borderRadius: 44px);
}
&__cancel-button {
.button(@width: 230px, @height: 72px, @fontSize: 28px, @fontWeight: 400, @borderRadius: 44px, @highlight: 0);
}
}

View File

@ -1,66 +0,0 @@
import { Button, ITouchEvent } from '@tarojs/components';
import { Popup } from '@taroify/core';
import { useCallback } from 'react';
import { BindPhoneStatus } from '@/components/login-button';
import PhoneButton from '@/components/phone-button';
import { ProtocolPrivacy } from '@/components/protocol-privacy';
import SafeBottomPadding from '@/components/safe-bottom-padding';
import './index.less';
interface IProps {
open: boolean;
onCancel: () => void;
onConfirm: (e: ITouchEvent) => void;
needPhone?: boolean;
needBindPhone?: boolean;
}
const PREFIX = 'agreement-popup';
export function AgreementPopup({ open, onCancel, onConfirm, needPhone, needBindPhone }: IProps) {
const handleBindPhone = useCallback(
(status: BindPhoneStatus, e: ITouchEvent) => {
if (status === BindPhoneStatus.Success) {
onConfirm(e);
} else {
onCancel();
}
},
[onCancel, onConfirm]
);
if (!open) {
return null;
}
return (
<Popup rounded className={PREFIX} placement="bottom" open={open} onClose={onCancel}>
<div className={`${PREFIX}__content`}>
<div className={`${PREFIX}__title`}></div>
<div className={`${PREFIX}__body`}>
<div></div>
<div>使</div>
</div>
<ProtocolPrivacy divider />
<div className={`${PREFIX}__btn-group`}>
<Button className={`${PREFIX}__cancel-button`} onClick={onCancel}>
</Button>
{needBindPhone ? (
<PhoneButton className={`${PREFIX}__confirm-button`} needPhone={needPhone} onBindPhone={handleBindPhone}>
</PhoneButton>
) : (
<Button className={`${PREFIX}__confirm-button`} onClick={onConfirm}>
</Button>
)}
</div>
</div>
<SafeBottomPadding />
</Popup>
);
}

View File

@ -9,7 +9,7 @@ import { PageUrl } from '@/constants/app';
import { MaterialViewSource, WORK_YEAR_LABELS } from '@/constants/material'; import { MaterialViewSource, WORK_YEAR_LABELS } from '@/constants/material';
import { AnchorInfo } from '@/types/material'; import { AnchorInfo } from '@/types/material';
import { calcDistance } from '@/utils/location'; import { calcDistance } from '@/utils/location';
import { getBasicInfo, getSalary } from '@/utils/material'; import { getBasicInfo } from '@/utils/material';
import { navigateTo } from '@/utils/route'; import { navigateTo } from '@/utils/route';
import { activeDate } from '@/utils/time'; import { activeDate } from '@/utils/time';
@ -18,23 +18,30 @@ import './index.less';
interface IProps { interface IProps {
data: AnchorInfo; data: AnchorInfo;
jobId?: string; jobId?: string;
validator: (onSuccess: () => void) => void;
} }
const PREFIX = 'anchor-card'; const PREFIX = 'anchor-card';
const getSalary = (data: AnchorInfo) => {
const { fullTimeMinPrice, fullTimeMaxPrice, partyTimeMinPrice, partyTimeMaxPrice } = data;
const prices: string[] = [];
if (fullTimeMinPrice && fullTimeMaxPrice) {
prices.push(`${fullTimeMinPrice / 1000}-${fullTimeMaxPrice / 1000}K/月`);
}
if (partyTimeMinPrice && partyTimeMaxPrice) {
prices.push(`${partyTimeMinPrice}-${partyTimeMaxPrice}/小时`);
}
return prices.filter(Boolean).join(' ');
};
function AnchorCard(props: IProps) { function AnchorCard(props: IProps) {
const { data, jobId, validator } = props; const { data, jobId } = props;
const style = data.isRead ? ({ '--read-color': '#999999' } as React.CSSProperties) : {}; const style = data.isRead ? ({ '--read-color': '#999999' } as React.CSSProperties) : {};
const cover = (data.materialVideoInfoList.find(video => video.isDefault) || data.materialVideoInfoList[0])?.coverUrl; const cover = (data.materialVideoInfoList.find(video => video.isDefault) || data.materialVideoInfoList[0])?.coverUrl;
const handleNavTo = useCallback(() => { const handleClick = useCallback(
navigateTo(PageUrl.MaterialView, { jobId, resumeId: data.id, source: MaterialViewSource.AnchorList }); () => navigateTo(PageUrl.MaterialView, { jobId, resumeId: data.id, source: MaterialViewSource.AnchorList }),
}, [data, jobId]); [data, jobId]
);
const handleClick = useCallback(() => {
validator(handleNavTo);
}, [handleNavTo, validator]);
return ( return (
<div className={PREFIX} style={style} onClick={handleClick}> <div className={PREFIX} style={style} onClick={handleClick}>

View File

@ -7,16 +7,11 @@ import { useCallback, useEffect, useRef, useState } from 'react';
import AnchorCard from '@/components/anchor-card'; import AnchorCard from '@/components/anchor-card';
import ListPlaceholder from '@/components/list-placeholder'; import ListPlaceholder from '@/components/list-placeholder';
import LoginDialog from '@/components/login-dialog';
import { EventName } from '@/constants/app'; import { EventName } from '@/constants/app';
import { AnchorSortType } from '@/constants/material'; import { AnchorSortType } from '@/constants/material';
import useUserInfo from '@/hooks/use-user-info';
import { AnchorInfo, GetAnchorListRequest, IAnchorFilters } from '@/types/material'; import { AnchorInfo, GetAnchorListRequest, IAnchorFilters } from '@/types/material';
import { logWithPrefix } from '@/utils/common'; import { logWithPrefix } from '@/utils/common';
import { requestAnchorList as requestData } from '@/utils/material'; import { requestAnchorList as requestData } from '@/utils/material';
import { getAgreementSigned, isNeedLogin, setAgreementSigned } from '@/utils/user';
import { AgreementPopup } from '../agreement-popup';
import './index.less'; import './index.less';
@ -60,11 +55,6 @@ function AnchorList(props: IAnchorListProps) {
const requestProps = useRef<IRequestProps>({}); const requestProps = useRef<IRequestProps>({});
const prevRequestProps = useRef<IRequestProps>({}); const prevRequestProps = useRef<IRequestProps>({});
const onListEmptyRef = useRef(onListEmpty); const onListEmptyRef = useRef(onListEmpty);
const [openLogin, setLoginOpen] = useState(false);
const [openAssignment, setAssignmentOpen] = useState(false);
const successCallback = useRef<() => void>(() => {});
const userInfo = useUserInfo();
const needLogin = isNeedLogin(userInfo);
const handleRefresh = useCallback(async () => { const handleRefresh = useCallback(async () => {
log('start pull refresh'); log('start pull refresh');
@ -130,37 +120,6 @@ function AnchorList(props: IAnchorListProps) {
[dataList] [dataList]
); );
const handleCancel = useCallback(() => {
setAssignmentOpen(false);
setAgreementSigned(false);
}, []);
const handleConfirm = useCallback(() => {
setAssignmentOpen(false);
setAgreementSigned(true);
successCallback.current();
}, []);
const handleLoginSuccess = useCallback(() => {
setLoginOpen(false);
successCallback.current();
}, []);
const handleLoginCancel = useCallback(() => {
setLoginOpen(false);
}, []);
const validator = (onSuccess: () => void) => {
successCallback.current = onSuccess;
if (!getAgreementSigned()) {
setAssignmentOpen(true);
} else if (needLogin) {
setLoginOpen(true);
} else {
onSuccess();
}
};
useEffect(() => { useEffect(() => {
onListEmptyRef.current = onListEmpty; onListEmptyRef.current = onListEmpty;
}, [onListEmpty]); }, [onListEmpty]);
@ -224,7 +183,6 @@ function AnchorList(props: IAnchorListProps) {
}, [jobId, ready, filters, cityCode, sortType]); }, [jobId, ready, filters, cityCode, sortType]);
return ( return (
<>
<PullRefresh <PullRefresh
className={classNames(`${PREFIX}__pull-refresh`, className)} className={classNames(`${PREFIX}__pull-refresh`, className)}
loading={refreshing} loading={refreshing}
@ -240,19 +198,11 @@ function AnchorList(props: IAnchorListProps) {
style={listHeight ? { height: `${listHeight}px` } : undefined} style={listHeight ? { height: `${listHeight}px` } : undefined}
> >
{dataList.map(item => ( {dataList.map(item => (
<AnchorCard data={item} jobId={jobId} key={item.id} validator={validator} /> <AnchorCard data={item} jobId={jobId} key={item.id} />
))} ))}
<ListPlaceholder hasMore={hasMore} loadingMore={loadingMore} loadMoreError={loadMoreError} /> <ListPlaceholder hasMore={hasMore} loadingMore={loadingMore} loadMoreError={loadMoreError} />
</List> </List>
</PullRefresh> </PullRefresh>
<AgreementPopup
open={openAssignment}
onCancel={handleCancel}
onConfirm={handleConfirm}
needBindPhone={needLogin}
/>
{openLogin && <LoginDialog onCancel={handleLoginCancel} onSuccess={handleLoginSuccess} />}
</>
); );
} }

View File

@ -5,19 +5,34 @@ import { isEqual } from 'lodash-es';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import PickerToolbar from '@/components/picker-toolbar'; import PickerToolbar from '@/components/picker-toolbar';
import { FULL_PRICE_OPTIONS, PART_PRICE_OPTIONS } from '@/constants/job'; import {
import { ALL_ANCHOR_READ_TYPES, ANCHOR_READ_TITLE_MAP, AnchorReadType } from '@/constants/material'; EmployType,
ALL_EMPLOY_TYPES,
FULL_PRICE_OPTIONS,
PART_PRICE_OPTIONS,
EMPLOY_TYPE_TITLE_MAP,
} from '@/constants/job';
import {
ALL_ANCHOR_READ_TYPES,
ALL_GENDER_TYPES,
ANCHOR_READ_TITLE_MAP,
AnchorReadType,
GENDER_TYPE_TITLE_MAP,
GenderType,
} from '@/constants/material';
import { IAnchorFilters } from '@/types/material'; import { IAnchorFilters } from '@/types/material';
import { isUndefined } from '@/utils/common';
import './index.less'; import './index.less';
type MoreFilter = Omit<IAnchorFilters, 'employType' | 'gender'>;
interface IProps { interface IProps {
value: MoreFilter; value: IAnchorFilters;
onConfirm: (newValue: MoreFilter) => void; onConfirm: (newValue: IAnchorFilters) => void;
} }
const PREFIX = 'anchor-picker'; const PREFIX = 'anchor-picker';
const getDefaultGender = (value: IAnchorFilters) => value.gender;
const getDefaultEmploy = (value: IAnchorFilters) => value.employType;
const getDefaultReadType = (value: IAnchorFilters) => value.readType; const getDefaultReadType = (value: IAnchorFilters) => value.readType;
const getDefaultCategory = (value: IAnchorFilters) => value.category || ''; const getDefaultCategory = (value: IAnchorFilters) => value.category || '';
const getSalaryValue = (value: IAnchorFilters, full: boolean) => { const getSalaryValue = (value: IAnchorFilters, full: boolean) => {
@ -32,7 +47,9 @@ const getSalaryValue = (value: IAnchorFilters, full: boolean) => {
function AnchorPicker(props: IProps) { function AnchorPicker(props: IProps) {
const { value, onConfirm } = props; const { value, onConfirm } = props;
const [gender, setGender] = useState<GenderType | undefined>(getDefaultGender(value));
const [readType, setReadType] = useState<AnchorReadType | undefined>(getDefaultReadType(value)); const [readType, setReadType] = useState<AnchorReadType | undefined>(getDefaultReadType(value));
const [employType, setEmployType] = useState<EmployType | undefined>(getDefaultEmploy(value));
const [fullSalary, setFullSalary] = useState(getSalaryValue(value, true)); const [fullSalary, setFullSalary] = useState(getSalaryValue(value, true));
const [partSalary, setPartSalary] = useState(getSalaryValue(value, false)); const [partSalary, setPartSalary] = useState(getSalaryValue(value, false));
const [category, setCategory] = useState(getDefaultCategory(value)); const [category, setCategory] = useState(getDefaultCategory(value));
@ -42,7 +59,9 @@ function AnchorPicker(props: IProps) {
}, []); }, []);
const handleClickReset = useCallback(() => { const handleClickReset = useCallback(() => {
setGender(undefined);
setReadType(undefined); setReadType(undefined);
setEmployType(undefined);
setFullSalary(null); setFullSalary(null);
setPartSalary(null); setPartSalary(null);
setCategory(''); setCategory('');
@ -64,6 +83,10 @@ function AnchorPicker(props: IProps) {
const handleClickConfirm = useCallback(() => { const handleClickConfirm = useCallback(() => {
const filters: IAnchorFilters = {}; const filters: IAnchorFilters = {};
if (!isUndefined(gender)) {
filters.gender = gender === GenderType.All ? undefined : gender;
}
employType && (filters.employType = employType);
readType && (filters.readType = readType); readType && (filters.readType = readType);
category && (filters.category = category); category && (filters.category = category);
if (fullSalary) { if (fullSalary) {
@ -75,10 +98,34 @@ function AnchorPicker(props: IProps) {
filters.highPriceForPartyTime = partSalary.maxSalary; filters.highPriceForPartyTime = partSalary.maxSalary;
} }
onConfirm(filters); onConfirm(filters);
}, [readType, category, fullSalary, partSalary, onConfirm]); }, [gender, employType, readType, category, fullSalary, partSalary, onConfirm]);
return ( return (
<div className={PREFIX}> <div className={PREFIX}>
<div className={`${PREFIX}__title`}></div>
<div className={`${PREFIX}__container`}>
{ALL_GENDER_TYPES.map((type: GenderType) => (
<div
key={type}
onClick={() => setGender(type)}
className={classNames(`${PREFIX}__item`, { selected: type === gender })}
>
{GENDER_TYPE_TITLE_MAP[type]}
</div>
))}
</div>
<div className={`${PREFIX}__title`}>/</div>
<div className={`${PREFIX}__container`}>
{ALL_EMPLOY_TYPES.map(type => (
<div
key={type}
onClick={() => setEmployType(type)}
className={classNames(`${PREFIX}__item`, { selected: type === employType })}
>
{EMPLOY_TYPE_TITLE_MAP[type]}
</div>
))}
</div>
<div className={`${PREFIX}__title`}>/</div> <div className={`${PREFIX}__title`}>/</div>
<div className={`${PREFIX}__container`}> <div className={`${PREFIX}__container`}>
{ALL_ANCHOR_READ_TYPES.map(type => ( {ALL_ANCHOR_READ_TYPES.map(type => (

View File

@ -2,36 +2,20 @@
@import '@/styles/variables.less'; @import '@/styles/variables.less';
.profile-checkbox { .profile-checkbox {
&__group { &__group {
width: 100%; width: 100%;
height: 100%; height: 100%;
.flex-row(); .flex-row();
flex-wrap: wrap;
gap: 24px;
} }
&__item { &__item {
flex: 0 1 auto; flex: 1;
padding: 34px 36px; height: 100%;
line-height: 32px;
border-radius: 16px;
border: 2px solid #fff;
background: #fff;
margin-right: 0;
margin-bottom: 0;
box-sizing: border-box;
&.active {
background: rgba(109, 61, 245, 0.12);
border: 2px solid #6d3df5;
.taroify-checkbox__label {
color: #6d3df5;
}
} }
.taroify-checkbox__label { &__icon {
line-height: 32px; width: 36px;
margin-left: 0; height: 36px;
}
} }
} }

View File

@ -1,44 +1,42 @@
import { Image } from '@tarojs/components';
import { Checkbox } from '@taroify/core'; import { Checkbox } from '@taroify/core';
import { CheckboxProps, CheckboxGroupProps } from '@taroify/core/checkbox'; import { CheckboxProps, CheckboxGroupProps } from '@taroify/core/checkbox';
import CheckboxGroupContext from '@taroify/core/checkbox/checkbox-group.context';
import { useContext } from 'react';
import './index.less'; import './index.less';
interface IProps extends CheckboxProps { interface IProps extends CheckboxProps {
text: string; text: string;
}
interface IGroupProps extends CheckboxGroupProps {
value: BL.Anything[]; value: BL.Anything[];
} }
interface IGroupProps extends CheckboxGroupProps {}
const PREFIX = 'profile-checkbox'; const PREFIX = 'profile-checkbox';
export function BlCheckboxGroup({ value, onChange, children }: IGroupProps) { export function BlCheckboxGroup(props: IGroupProps) {
return ( return <Checkbox.Group className={`${PREFIX}__group`} direction="horizontal" {...props} />;
<Checkbox.Group className={`${PREFIX}__group`} value={value} onChange={onChange}>
{children}
</Checkbox.Group>
);
} }
export function BlCheckbox(props: IProps) { export function BlCheckbox(props: IProps) {
const { name, text } = props; const { name, text, value } = props;
const { value: names = [], onChange: onNamesChange } = useContext(CheckboxGroupContext);
const checked = names.includes(name);
const handleClick = () => {
if (!name) return;
if (checked) {
onNamesChange?.(names.filter(aName => aName !== name));
} else {
onNamesChange?.([...names, name]);
}
};
return ( return (
<Checkbox className={`${PREFIX}__item ${checked ? 'active' : ''}`} name={name} icon={null} onClick={handleClick}> <Checkbox
className={`${PREFIX}__item`}
name={name}
icon={
<Image
className={`${PREFIX}__icon`}
mode="aspectFit"
src={
value.includes(name)
? require('@/statics/svg/radio-checked.svg')
: require('@/statics/svg/radio-uncheck.svg')
}
/>
}
>
{text} {text}
</Checkbox> </Checkbox>
); );

View File

@ -1,13 +0,0 @@
import Select, { ISelectProps } from '@/components/select';
import { EmployType, JOB_EMPLOY_TYPE_OPTIONS } from '@/constants/job';
interface IProps extends Omit<ISelectProps<EmployType>, 'options'> {
value: EmployType;
}
function EmployTypeSelect(props: IProps) {
const { value: selectValue, onSelect } = props;
return <Select options={JOB_EMPLOY_TYPE_OPTIONS} title="类型" value={selectValue} onSelect={onSelect} />;
}
export default EmployTypeSelect;

View File

@ -1,13 +0,0 @@
import Select, { ISelectProps } from '@/components/select';
import { GENDER_OPTIONS, GenderType } from '@/constants/material';
interface IProps extends Omit<ISelectProps<GenderType>, 'options'> {
value: GenderType;
}
function GenderSelect(props: IProps) {
const { value: selectValue, onSelect } = props;
return <Select options={GENDER_OPTIONS} title="性别" value={selectValue} onSelect={onSelect} />;
}
export default GenderSelect;

View File

@ -1,85 +0,0 @@
@import '@/styles/common.less';
.group-certification-list {
min-height: calc(100vh - 98rpx);
&__banner {
font-weight: 400;
font-size: 24px;
height: 74px;
padding: 32px 32px 25px;
line-height: 36px;
color: #999999;
background: #F5F6FA;
position: fixed;
z-index: 1;
top: 99px;
left: 0;
right: 0;
}
&__title {
height: 72px;
width: 100%;
padding: 0 24px;
box-sizing: border-box;
line-height: 72px;
font-size: 24px;
color: rgba(0, 0, 0, 0.5);
position: fixed;
top: 227rpx;
left: 0;
z-index: 1;
background: #fff;
&-border {
border-bottom: 1px solid #e6e7e8;
.flex-row();
}
&-time {
padding: 0 8px;
flex: 0 0 120px;
width: 120px;
flex-shrink: 0;
}
&-name {
text-align: right;
flex: 1;
}
}
&__pull-refresh {
margin-top: 204px;
}
&__item {
height: 100px;
width: 100%;
padding: 24px 32px 0 32px;
box-sizing: border-box;
font-size: 28px;
background: #fff;
&-border {
border-bottom: 1px solid #e6e7e8;
}
&-content {
.flex-row();
width: 100%;
padding-bottom: 24px;
}
&-time {
padding: 0 8px;
flex: 0 0 120px;
width: 120px;
flex-shrink: 0;
}
&-name {
text-align: right;
flex: 1;
}
}
}

View File

@ -1,153 +0,0 @@
import { List, PullRefresh } from '@taroify/core';
import classNames from 'classnames';
import { useCallback, useEffect, useRef, useState } from 'react';
import ListPlaceholder from '@/components/list-placeholder';
import { AuthedGroupInfo } from '@/types/partner';
import { logWithPrefix } from '@/utils/common';
import { formatTimestamp, getAuthedGroupList as requestData } from '@/utils/partner';
import './index.less';
const PREFIX = 'group-certification-list';
const log = logWithPrefix(PREFIX);
const FIRST_PAGE = 0;
function GroupCertificationList(props: {
refreshDisabled?: boolean;
visible?: boolean;
listHeight?: number;
className?: string;
onListEmpty?: () => void;
}) {
const { className, listHeight, refreshDisabled, visible = true, onListEmpty } = props;
const [hasMore, setHasMore] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [loadingMore, setLoadingMore] = useState(false);
const [loadMoreError, setLoadMoreError] = useState(false);
const [dataList, setDataList] = useState<AuthedGroupInfo[]>([]);
const currentPage = useRef<number>(FIRST_PAGE);
const onListEmptyRef = useRef(onListEmpty);
const handleRefresh = useCallback(async () => {
log('start pull refresh');
try {
setRefreshing(true);
setLoadMoreError(false);
const content = await requestData();
setDataList(content);
currentPage.current = 1;
// setHasMore(currentPage.current < totalPages);
!content.length && onListEmptyRef.current?.();
log('pull refresh success');
} catch (e) {
setDataList([]);
setHasMore(false);
setLoadMoreError(true);
currentPage.current = FIRST_PAGE;
log('pull refresh failed');
} finally {
setRefreshing(false);
}
}, []);
const handleLoadMore = useCallback(async () => {
log('start load more', hasMore);
if (!hasMore) {
return;
}
setLoadMoreError(false);
setLoadingMore(true);
try {
const content = await requestData();
setDataList([...dataList, ...content]);
currentPage.current = currentPage.current + 1;
// setHasMore(currentPage.current < totalPages);
log('load more success');
} catch (e) {
setLoadMoreError(true);
log('load more failed');
} finally {
setLoadingMore(false);
}
}, [dataList, hasMore]);
useEffect(() => {
onListEmptyRef.current = onListEmpty;
}, [onListEmpty]);
// 初始化数据&配置变更后刷新数据
useEffect(() => {
// 列表不可见时,先不做处理
if (!visible) {
log('visible changed, but is not visible, only clear list');
return;
}
const refresh = async () => {
log('visible changed, start refresh list data');
try {
setDataList([]);
setLoadingMore(true);
setLoadMoreError(false);
const content = await requestData();
setDataList(content);
currentPage.current = 1;
// setHasMore(currentPage.current < totalPages);
!content.length && onListEmptyRef.current?.();
} catch (e) {
setDataList([]);
setHasMore(false);
setLoadMoreError(true);
} finally {
log('visible changed, refresh list data end');
setLoadingMore(false);
}
};
refresh();
}, [visible]);
return (
<div className={PREFIX}>
<div className={`${PREFIX}__banner`}>
</div>
<div className={`${PREFIX}__title`}>
<div className={`${PREFIX}__title-border`}>
<div className={`${PREFIX}__title-time`}></div>
<div className={`${PREFIX}__title-name`}></div>
</div>
</div>
<PullRefresh
className={classNames(`${PREFIX}__pull-refresh`, className)}
loading={refreshing}
onRefresh={handleRefresh}
disabled={refreshDisabled}
>
<List
hasMore={hasMore}
onLoad={handleLoadMore}
loading={loadingMore || refreshing}
disabled={loadMoreError || !visible}
fixedHeight={typeof listHeight !== 'undefined'}
style={listHeight ? { height: `${listHeight}px` } : undefined}
>
{dataList.map(item => (
<div className={`${PREFIX}__item`} key={item.openGid}>
<div className={`${PREFIX}__item-border`}>
<div className={`${PREFIX}__item-content`}>
<div className={`${PREFIX}__item-time`}>{formatTimestamp(item.authDate, true)}</div>
<div className={`${PREFIX}__item-name`}>{item.groupName}</div>
</div>
</div>
</div>
))}
<ListPlaceholder hasMore={false} loadingMore={loadingMore} loadMoreError={loadMoreError} />
</List>
</PullRefresh>
</div>
);
}
export default GroupCertificationList;

View File

@ -1,21 +1,18 @@
import React from 'react'; import React from 'react';
import BaseTabBar from '@/components/tab-bar'; import BaseTabBar from '@/components/tab-bar';
import { PageType } from '@/constants/app';
import './index.less'; import './index.less';
interface IProps extends React.PropsWithChildren { interface IProps extends React.PropsWithChildren {}
type: PageType;
}
export default function HomePage(props: IProps) { export default function HomePage(props: IProps) {
const { children, type } = props; const { children } = props;
return ( return (
<React.Fragment> <React.Fragment>
{children} {children}
<BaseTabBar type={type} /> <BaseTabBar />
</React.Fragment> </React.Fragment>
); );
} }

View File

@ -1,22 +0,0 @@
@import '@/styles/variables.less';
@import '@/styles/common.less';
.invite-operations-fragment-banner {
width: 100%;
height: 90px;
position: relative;
margin-bottom: 24px;
&__image {
width: 100%;
height: 100%;
}
&__close {
width: 64px;
height: 38px;
position: absolute;
right: 0;
top: 0;
}
}

View File

@ -1,40 +0,0 @@
import { Image } from '@tarojs/components';
import { useCallback, useState } from 'react';
import { PageUrl } from '@/constants/app';
import { getPartnerBannerClose, setPartnerBannerClose } from '@/utils/partner';
import { navigateTo } from '@/utils/route';
import './index.less';
const PREFIX = 'invite-operations-fragment-banner';
export default function InviteOperationsBanner() {
const [bannerClose, setBannerClose] = useState<boolean>(getPartnerBannerClose());
const handlePartnerBannerClose = useCallback(e => {
e.preventDefault();
e.stopPropagation();
setBannerClose(true);
setPartnerBannerClose();
}, []);
const handleClick = useCallback(async () => {
navigateTo(PageUrl.InviteOperations);
}, []);
if (bannerClose) {
return null;
}
return (
<div className={PREFIX} onClick={handleClick}>
<Image
className={`${PREFIX}__image`}
src="https://publiccdn.neighbourhood.com.cn/img/invite-operations-banner.png"
mode="scaleToFill"
/>
<div className={`${PREFIX}__close`} onClick={handlePartnerBannerClose} />
</div>
);
}

View File

@ -11,16 +11,6 @@
border-radius: 16px; border-radius: 16px;
background: #FFFFFF; background: #FFFFFF;
margin-bottom: 24px; margin-bottom: 24px;
position: relative;
}
&__mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.4);
} }
&__header { &__header {

View File

@ -5,11 +5,11 @@ import React, { useCallback } from 'react';
import { CertificationStatusIcon } from '@/components/certification-status'; import { CertificationStatusIcon } from '@/components/certification-status';
import { PageUrl } from '@/constants/app'; import { PageUrl } from '@/constants/app';
// import { CITY_CODE_TO_NAME_MAP, COUNTY_CODE_TO_NAME_MAP } from '@/constants/city'; import { CITY_CODE_TO_NAME_MAP, COUNTY_CODE_TO_NAME_MAP } from '@/constants/city';
import { CertificationStatusType } from '@/constants/company'; import { CertificationStatusType } from '@/constants/company';
import { EMPLOY_TYPE_TITLE_MAP, EmployType } from '@/constants/job'; import { EMPLOY_TYPE_TITLE_MAP, EmployType } from '@/constants/job';
import { JobInfo } from '@/types/job'; import { JobInfo } from '@/types/job';
// import { LocationInfo } from '@/types/location'; import { LocationInfo } from '@/types/location';
import { getJobSalary, getJobTitle } from '@/utils/job'; import { getJobSalary, getJobTitle } from '@/utils/job';
import { calcDistance } from '@/utils/location'; import { calcDistance } from '@/utils/location';
import { navigateTo, redirectTo } from '@/utils/route'; import { navigateTo, redirectTo } from '@/utils/route';
@ -23,16 +23,16 @@ interface IProps {
} }
const PREFIX = 'job-card'; const PREFIX = 'job-card';
// const getCityDes = (location: LocationInfo) => { const getCityDes = (location: LocationInfo) => {
// if (!location) { if (!location) {
// return ''; return '';
// } }
// let des = CITY_CODE_TO_NAME_MAP.get(location.cityCode); let des = CITY_CODE_TO_NAME_MAP.get(location.cityCode);
// if (location.countyCode) { if (location.countyCode) {
// des += `-${COUNTY_CODE_TO_NAME_MAP.get(location.countyCode)}`; des += `-${COUNTY_CODE_TO_NAME_MAP.get(location.countyCode)}`;
// } }
// return des; return des;
// }; };
function JobCard(props: IProps) { function JobCard(props: IProps) {
const { className, data, redirectOpen } = props; const { className, data, redirectOpen } = props;
@ -102,9 +102,8 @@ function JobCard(props: IProps) {
/> />
<div className={`${PREFIX}__publisher-name`}>{publisher}</div> <div className={`${PREFIX}__publisher-name`}>{publisher}</div>
</div> </div>
{/*<div className={`${PREFIX}__city`}>{getCityDes(jobLocation)}</div>*/} <div className={`${PREFIX}__city`}>{getCityDes(jobLocation)}</div>
</div> </div>
{data.isRead && <div className={`${PREFIX}__mask`} />}
</div> </div>
); );
} }

View File

@ -1,5 +1,3 @@
import Taro from '@tarojs/taro';
import { List, PullRefresh } from '@taroify/core'; import { List, PullRefresh } from '@taroify/core';
import classNames from 'classnames'; import classNames from 'classnames';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
@ -7,7 +5,6 @@ import { useCallback, useEffect, useRef, useState } from 'react';
import JobCard from '@/components/job-card'; import JobCard from '@/components/job-card';
import ListPlaceholder from '@/components/list-placeholder'; import ListPlaceholder from '@/components/list-placeholder';
import { EventName } from '@/constants/app';
import { JobType, EmployType, SortType } from '@/constants/job'; import { JobType, EmployType, SortType } from '@/constants/job';
import { JobInfo, GetJobsRequest } from '@/types/job'; import { JobInfo, GetJobsRequest } from '@/types/job';
import { logWithPrefix } from '@/utils/common'; import { logWithPrefix } from '@/utils/common';
@ -38,7 +35,6 @@ function JobList(props: IJobListProps) {
visible = true, visible = true,
cityCode = 'ALL', cityCode = 'ALL',
category = JobType.All, category = JobType.All,
sourceType,
employType = EmployType.All, employType = EmployType.All,
sortType = SortType.RECOMMEND, sortType = SortType.RECOMMEND,
isFollow = false, isFollow = false,
@ -61,23 +57,6 @@ function JobList(props: IJobListProps) {
const prevRequestProps = useRef<IRequestProps>({}); const prevRequestProps = useRef<IRequestProps>({});
const onListEmptyRef = useRef(onListEmpty); const onListEmptyRef = useRef(onListEmpty);
const handleReadJob = useCallback(
(jobId: string) => {
const index = dataList.findIndex(d => String(d.id) === jobId);
if (index < 0) {
return;
}
const job = dataList[index];
if (!job || job.isRead) {
return;
}
log('auto mark read', jobId);
dataList.splice(index, 1, { ...job, isRead: true });
setDataList([...dataList]);
},
[dataList]
);
const handleRefresh = useCallback(async () => { const handleRefresh = useCallback(async () => {
log('start pull refresh'); log('start pull refresh');
try { try {
@ -129,20 +108,12 @@ function JobList(props: IJobListProps) {
onListEmptyRef.current = onListEmpty; onListEmptyRef.current = onListEmpty;
}, [onListEmpty]); }, [onListEmpty]);
useEffect(() => {
Taro.eventCenter.on(EventName.VIEW_JOB_SUCCESS, handleReadJob);
return () => {
Taro.eventCenter.off(EventName.VIEW_JOB_SUCCESS, handleReadJob);
};
}, [handleReadJob]);
useEffect(() => { useEffect(() => {
log('request params changed'); log('request params changed');
requestProps.current = { requestProps.current = {
category, category,
cityCode, cityCode,
employType, employType,
sourceType,
sortType, sortType,
isFollow, isFollow,
isOwner, isOwner,
@ -157,7 +128,6 @@ function JobList(props: IJobListProps) {
}, [ }, [
category, category,
cityCode, cityCode,
sourceType,
employType, employType,
sortType, sortType,
isFollow, isFollow,
@ -206,7 +176,7 @@ function JobList(props: IJobListProps) {
} }
}; };
refresh(); refresh();
}, [visible, cityCode, employType, sourceType, sortType, keyWord, minSalary, maxSalary, blGroupId]); }, [visible, cityCode, employType, sortType, keyWord, minSalary, maxSalary, blGroupId]);
// log('render', `hasMore: ${hasMore}, loadingMore: ${loadingMore}, refreshing: ${refreshing}`); // log('render', `hasMore: ${hasMore}, loadingMore: ${loadingMore}, refreshing: ${refreshing}`);

View File

@ -6,12 +6,12 @@
height: 152px; height: 152px;
.flex-row(); .flex-row();
padding: 24px 32px; padding: 24px 32px;
background: #fff; background: #FFF;
box-sizing: border-box; box-sizing: border-box;
position: relative; position: relative;
&::after { &::after {
content: ''; content: "";
height: 2px; height: 2px;
background: #00000026; background: #00000026;
position: absolute; position: absolute;
@ -52,11 +52,7 @@
&__status { &__status {
height: 100%; height: 100%;
display: flex; .flex-row();
flex-direction: column;
justify-content: space-between;
align-items: flex-end;
gap: 24px;
font-size: 28px; font-size: 28px;
line-height: 32px; line-height: 32px;
font-weight: 400; font-weight: 400;
@ -67,27 +63,7 @@
} }
&.error { &.error {
color: #ff5051; color: #FF5051;
}
}
&__buy-tag {
padding: 8px;
background: rgba(255, 80, 81, 0.12);
border-radius: 4px;
font-size: 24px;
line-height: 24px;
color: #ff5051;
.flex-row();
&-wrapper {
padding-top: 8px;
}
&-icon {
width: 13px;
height: 18px;
margin-right: 5px;
} }
} }
} }

View File

@ -1,13 +1,8 @@
import { Image } from '@tarojs/components';
import { Arrow } from '@taroify/icons';
import classNames from 'classnames'; import classNames from 'classnames';
import React, { useCallback, useMemo } from 'react'; import React, { useCallback } from 'react';
// import { cityValues } from '@/components/user-batch-publish';
import { PageUrl } from '@/constants/app'; import { PageUrl } from '@/constants/app';
import { JOB_MANAGE_STATUS_TITLE_MAP, JobManageStatus } from '@/constants/job'; import { JOB_MANAGE_STATUS_TITLE_MAP, JobManageStatus } from '@/constants/job';
import useCityOperators from '@/hooks/use-city-operators';
import { JobManageInfo } from '@/types/job'; import { JobManageInfo } from '@/types/job';
import { getJobLocation } from '@/utils/job'; import { getJobLocation } from '@/utils/job';
import { navigateTo } from '@/utils/route'; import { navigateTo } from '@/utils/route';
@ -30,32 +25,6 @@ const STATUS_CLASS_MAP = {
[JobManageStatus.Expire]: 'close', [JobManageStatus.Expire]: 'close',
}; };
function GoBatchTag({ cityCode, jobId }: { cityCode: string; jobId: JobManageInfo['id'] }) {
const cityOperators = useCityOperators();
const availableCities = useMemo(() => cityOperators.filter(c => c.sendCount), [cityOperators]);
const handleClick = useCallback(
e => {
e.preventDefault();
e.stopPropagation();
navigateTo(PageUrl.AccelerateDelegatePublish, { cityCode, jobId });
},
[cityCode, jobId]
);
if (!availableCities.find(c => c.cityCode === cityCode)) {
return null;
}
return (
<div onClick={handleClick} className={`${PREFIX}__buy-tag-wrapper`}>
<div className={`${PREFIX}__buy-tag`}>
<Image className={`${PREFIX}__buy-tag-icon`} src="https://publiccdn.neighbourhood.com.cn/img/lightning.svg" />
<div></div>
<Arrow size={12} />
</div>
</div>
);
}
function JobManageCard(props: IProps) { function JobManageCard(props: IProps) {
const { data = {} } = props; const { data = {} } = props;
const { id, title, status } = data as JobManageInfo; const { id, title, status } = data as JobManageInfo;
@ -72,7 +41,6 @@ function JobManageCard(props: IProps) {
</div> </div>
<div className={classNames(`${PREFIX}__status`, { [STATUS_CLASS_MAP[status]]: true })}> <div className={classNames(`${PREFIX}__status`, { [STATUS_CLASS_MAP[status]]: true })}>
<div>{JOB_MANAGE_STATUS_TITLE_MAP[status]}</div> <div>{JOB_MANAGE_STATUS_TITLE_MAP[status]}</div>
<GoBatchTag cityCode={props.data.cityCode} jobId={props.data.id} />
</div> </div>
</div> </div>
); );

View File

@ -1,28 +1,2 @@
@import '@/styles/common.less'; @import '@/styles/common.less';
@import '@/styles/variables.less'; @import '@/styles/variables.less';
.job-manage-list {
&__empty-tips {
width: 100%;
height: 100vh;
padding-top: 218px;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
position: absolute;
&__icon {
width: 386px;
height: 278px;
}
&__describe {
font-size: 28px;
font-weight: 500;
line-height: 40px;
color: @blColor;
margin-top: 50px;
}
}
}

View File

@ -1,4 +1,3 @@
import { Image } from '@tarojs/components';
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import { List, PullRefresh } from '@taroify/core'; import { List, PullRefresh } from '@taroify/core';
@ -29,15 +28,7 @@ const FIRST_PAGE = 0;
const PAGE_SIZE = 40; const PAGE_SIZE = 40;
const PREFIX = 'job-manage-list'; const PREFIX = 'job-manage-list';
const log = logWithPrefix(PREFIX); const log = logWithPrefix(PREFIX);
const EmptyTips = (props: { className?: string; height?: number }) => {
const { className, height } = props;
return (
<div className={classNames(`${PREFIX}__empty-tips`, className)} style={height ? { height } : undefined}>
<Image className={`${PREFIX}__empty-tips__icon`} src={require('@/statics/svg/empty-box.svg')} mode="aspectFit" />
<div className={`${PREFIX}__empty-tips__describe`}></div>
</div>
);
};
function JobManageList(props: IJobManageListProps) { function JobManageList(props: IJobManageListProps) {
const { className, listHeight, refreshDisabled, visible = true, status, onListEmpty } = props; const { className, listHeight, refreshDisabled, visible = true, status, onListEmpty } = props;
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
@ -160,8 +151,6 @@ function JobManageList(props: IJobManageListProps) {
onRefresh={handleRefresh} onRefresh={handleRefresh}
disabled={refreshDisabled} disabled={refreshDisabled}
> >
{Boolean(!dataList.length) && <EmptyTips className={className} height={listHeight} />}
<List <List
hasMore={hasMore} hasMore={hasMore}
onLoad={handleLoadMore} onLoad={handleLoadMore}

View File

@ -46,8 +46,8 @@ function JobRecommendList(props: IJobListProps) {
setDataList([]); setDataList([]);
setLoading(true); setLoading(true);
setLoadError(false); setLoadError(false);
const { jobResults } = await requestMyRecommendJobList({ ...requestProps.current }); const { jobResults = [] } = await requestMyRecommendJobList({ ...requestProps.current });
setDataList(jobResults || []); setDataList(jobResults);
} catch (e) { } catch (e) {
setDataList([]); setDataList([]);
setLoadError(true); setLoadError(true);

View File

@ -1,41 +0,0 @@
@import '@/styles/common.less';
@import '@/styles/variables.less';
.join-group-hint {
.flex-row();
background: #FFFFFF;
border-radius: 16px;
padding: 32px 24px;
margin-top: 24px;
margin-bottom: 24px;
&__icon {
width: 80px;
height: 80px;
margin-right: 32px;
}
&__left {
flex: 1;
padding-left: 8px;
&-title {
font-weight: 500;
font-size: 32px;
line-height: 40px;
margin-bottom: 16px;
}
&-desc {
font-weight: 400;
font-size: 28px;
line-height: 40px;
}
}
&__right {
flex: 0 0 auto;
}
&__button {
.button(@width: 186px, @height: 64px, @fontSize: 28px, @fontWeight: 400, @borderRadius: 32px, @highlight: 0);
}
}

View File

@ -1,63 +0,0 @@
import { Button, Image } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { Plus } from '@taroify/icons';
import { useCallback, useState } from 'react';
import { RoleType } from '@/constants/app';
import { CacheKey } from '@/constants/cache-key';
import { CITY_CODE_TO_NAME_MAP } from '@/constants/city';
import useCityOperators from '@/hooks/use-city-operators';
import { getRoleTypeWithDefault } from '@/utils/app';
import { openCustomerServiceChat } from '@/utils/common';
import { getCurrentCityCode } from '@/utils/location';
import { checkCityCode, validCityCode } from '@/utils/user';
import './index.less';
const PREFIX = 'join-group-hint';
const DEFAULT_GROUP = {
name: '播络主播招聘群',
serviceUrl: 'https://work.weixin.qq.com/kfid/kfcc60ac7b6420787a8',
};
export function JoinGroupHint() {
const cityCode = getCurrentCityCode();
const roleType = getRoleTypeWithDefault();
const cityOperators = useCityOperators();
const group = cityOperators.find(g => String(g.cityCode) === cityCode);
const [clicked, setClicked] = useState(!!Taro.getStorageSync(CacheKey.JOIN_GROUP_CARD_CLICKED));
const handleClick = useCallback(() => {
if (group && !checkCityCode(cityCode)) {
return;
}
openCustomerServiceChat(group ? group.groupLink : DEFAULT_GROUP.serviceUrl);
Taro.setStorageSync(CacheKey.JOIN_GROUP_CARD_CLICKED, true);
setClicked(true);
}, [cityCode, group]);
if (!validCityCode(cityCode) || clicked) {
return null;
}
return (
<div className={PREFIX}>
<Image
className={`${PREFIX}__icon`}
src="https://publiccdn.neighbourhood.com.cn/img/group-avatar.png"
mode="aspectFit"
/>
<div className={`${PREFIX}__left`}>
<div className={`${PREFIX}__left-title`}>
{group ? `${CITY_CODE_TO_NAME_MAP.get(cityCode)}主播招聘群` : DEFAULT_GROUP.name}
</div>
<div className={`${PREFIX}__left-desc`}>{roleType === RoleType.Anchor ? '高薪工作早知道' : '免费招主播'}</div>
</div>
<div className={`${PREFIX}__right`}>
<Button className={`${PREFIX}__button`} onClick={handleClick}>
<Plus />
</Button>
</div>
</div>
);
}

View File

@ -1,12 +1,11 @@
import { Button, ButtonProps, ITouchEvent } from '@tarojs/components'; import { Button, ButtonProps } from '@tarojs/components';
import classNames from 'classnames'; import classNames from 'classnames';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { AgreementPopup } from '@/components/agreement-popup';
import LoginDialog from '@/components/login-dialog'; import LoginDialog from '@/components/login-dialog';
import useUserInfo from '@/hooks/use-user-info'; import useUserInfo from '@/hooks/use-user-info';
import { getAgreementSigned, isNeedLogin, requestUserInfo, setAgreementSigned } from '@/utils/user'; import { isNeedLogin } from '@/utils/user';
import './index.less'; import './index.less';
@ -18,83 +17,34 @@ export enum BindPhoneStatus {
export interface ILoginButtonProps extends ButtonProps { export interface ILoginButtonProps extends ButtonProps {
needPhone?: boolean; needPhone?: boolean;
needRefresh?: boolean;
onRefresh?: (() => void) | (() => Promise<void>);
needAssignment?: boolean;
} }
const PREFIX = 'login-button'; const PREFIX = 'login-button';
function LoginButton(props: ILoginButtonProps) { function LoginButton(props: ILoginButtonProps) {
const { className, children, needPhone, onRefresh, onClick, needRefresh, ...otherProps } = props; const { className, children, needPhone, onClick, ...otherProps } = props;
const userInfo = useUserInfo(); const userInfo = useUserInfo();
const [loginVisible, setLoginVisible] = useState(false); const [visible, setVisible] = useState(false);
const [assignVisible, setAssignVisible] = useState(false);
const needLogin = isNeedLogin(userInfo); const needLogin = isNeedLogin(userInfo);
// 点击按钮时,协议同意也手机也授权了 -> 下一步
//
// 点击按钮时,协议同意但是手机授权没给 -> 弹登录
// -> 如果授权了手机号就进行下一步
// -> 不给授权没下一步
//
// 点击按钮时,协议不同意 -> 弹协议窗口
// -> 如果同意就进行下一步
// -> 不同意没下一步
const handleClick = useCallback(
(e: ITouchEvent) => {
if (!getAgreementSigned()) {
setAssignVisible(true);
} else if (needLogin) {
setLoginVisible(true);
} else if (onClick) {
onClick(e);
}
},
[needLogin, onClick]
);
const handleConfirm = useCallback( const onSuccess = useCallback(
(e: ITouchEvent) => { e => {
setAssignVisible(false); setVisible(false);
setAgreementSigned(true); onClick?.(e);
if (onClick) {
onClick(e);
}
}, },
[onClick] [onClick]
); );
const handleCancel = useCallback(() => {
setAgreementSigned(false);
setAssignVisible(false);
}, []);
const handleLoginSuccess = useCallback(
async e => {
setLoginVisible(false);
if (needRefresh) {
requestUserInfo();
onRefresh && (await onRefresh());
}
onClick?.(e);
},
[needRefresh, onClick]
);
return ( return (
<> <>
<Button {...otherProps} className={classNames(PREFIX, className)} onClick={handleClick}> <Button
{...otherProps}
className={classNames(PREFIX, className)}
onClick={needLogin ? () => setVisible(true) : onClick}
>
{children} {children}
</Button> </Button>
<AgreementPopup {visible && <LoginDialog onCancel={() => setVisible(false)} onSuccess={onSuccess} needPhone={needPhone} />}
open={assignVisible}
onCancel={handleCancel}
onConfirm={handleConfirm}
needBindPhone={needLogin}
/>
{loginVisible && (
<LoginDialog onCancel={() => setLoginVisible(false)} onSuccess={handleLoginSuccess} needPhone={needPhone} />
)}
</> </>
); );
} }

View File

@ -17,4 +17,22 @@
.button(@width: 360px, @height: 72px, @fontSize: 28px, @fontWeight: 400, @borderRadius: 44px); .button(@width: 360px, @height: 72px, @fontSize: 28px, @fontWeight: 400, @borderRadius: 44px);
margin-top: 40px; margin-top: 40px;
} }
&__cancel-button {
min-width: fit-content;
font-size: 28px;
line-height: 32px;
color: @blHighlightColor;
background: transparent;
border: none;
margin-top: 40px;
&::after {
border-color: transparent
}
}
&__checkbox {
margin-top: 40px;
}
} }

View File

@ -1,6 +1,11 @@
import { Button } from '@tarojs/components';
import { Dialog } from '@taroify/core'; import { Dialog } from '@taroify/core';
import { useCallback, useState } from 'react';
import PhoneButton, { IPhoneButtonProps } from '@/components/phone-button'; import PhoneButton, { IPhoneButtonProps } from '@/components/phone-button';
import { ProtocolPrivacyCheckbox } from '@/components/protocol-privacy';
import Toast from '@/utils/toast';
import './index.less'; import './index.less';
@ -16,12 +21,23 @@ const PREFIX = 'login-dialog';
export default function LoginDialog(props: IProps) { export default function LoginDialog(props: IProps) {
const { title = '使用播络服务前,请先登录', needPhone, onSuccess, onCancel, onBindPhone } = props; const { title = '使用播络服务前,请先登录', needPhone, onSuccess, onCancel, onBindPhone } = props;
const [checked, setChecked] = useState(false);
const handleTipCheck = useCallback(() => {
Toast.info('请先阅读并同意协议');
}, []);
return ( return (
<Dialog open onClose={onCancel} className={PREFIX}> <Dialog open onClose={onCancel}>
<Dialog.Content> <Dialog.Content>
<div className={`${PREFIX}__container`}> <div className={`${PREFIX}__container`}>
<div className={`${PREFIX}__title`}>{title}</div> <div className={`${PREFIX}__title`}>{title}</div>
{!checked && (
<Button className={`${PREFIX}__confirm-button`} onClick={handleTipCheck}>
</Button>
)}
{checked && (
<PhoneButton <PhoneButton
className={`${PREFIX}__confirm-button`} className={`${PREFIX}__confirm-button`}
onSuccess={onSuccess} onSuccess={onSuccess}
@ -30,6 +46,11 @@ export default function LoginDialog(props: IProps) {
> >
</PhoneButton> </PhoneButton>
)}
<Button className={`${PREFIX}__cancel-button`} onClick={onCancel}>
</Button>
<ProtocolPrivacyCheckbox checked={checked} onChange={setChecked} className={`${PREFIX}__checkbox`} />
</div> </div>
</Dialog.Content> </Dialog.Content>
</Dialog> </Dialog>

View File

@ -4,7 +4,7 @@
.material-card { .material-card {
padding: 32px 24px; padding: 32px 24px;
border-radius: 16px; border-radius: 16px;
background: #ffffff; background: #FFFFFF;
box-sizing: border-box; box-sizing: border-box;
&__header { &__header {
@ -16,10 +16,6 @@
.flex-row(); .flex-row();
} }
&__right {
color: @blHighlightColor;
}
&__title { &__title {
font-size: 32px; font-size: 32px;
line-height: 40px; line-height: 40px;
@ -54,7 +50,7 @@
&__body { &__body {
width: 100%; width: 100%;
height: 160px; height: 156px;
margin-top: 24px; margin-top: 24px;
.flex-column(); .flex-column();
justify-content: center; justify-content: center;
@ -84,44 +80,38 @@
} }
} }
&__info { &__scroll-view {
.flex-row(); position: relative;
gap: 24px;
width: 100%;
&-left {
width: 125px;
height: 160px;
}
&-cover {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 12px; .flex-row();
}
&-right {
.name {
font-style: normal;
font-weight: 500;
font-size: 28px;
line-height: 32px;
color: @blColor;
margin-bottom: 12px;
}
.info,
.worked {
font-weight: 400;
font-size: 24px;
line-height: 36px;
color: @blColorG2;
margin-bottom: 8px;
}
.salary {
font-weight: 500;
font-size: 24px;
line-height: 36px;
color: @blColor;
&::-webkit-scrollbar {
display: none;
}
&::after {
content: '';
position: absolute;
right: 0;
width: 102px;
height: 100%;
background: linear-gradient(91.41deg, rgba(255, 255, 255, 0) 1.86%, #FFFFFF 99.47%);
} }
} }
&__cover-list {
height: 100%;
.flex-row();
} }
&__cover-image {
width: 120px;
height: 100%;
margin-right: 24px;
// 不知道为啥高度不对,可能 scroll-view 默认底部是滚动条高度?
margin-top: 38px;
border-radius: 8px;
}
} }

View File

@ -1,4 +1,4 @@
import { Image } from '@tarojs/components'; import { Image, ScrollView } from '@tarojs/components';
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import { Loading } from '@taroify/core'; import { Loading } from '@taroify/core';
@ -9,12 +9,11 @@ import { useCallback, useEffect, useRef, useState } from 'react';
import LoginButton from '@/components/login-button'; import LoginButton from '@/components/login-button';
import { EventName, PageUrl } from '@/constants/app'; import { EventName, PageUrl } from '@/constants/app';
import { CollectEventName, ReportEventId } from '@/constants/event'; import { CollectEventName, ReportEventId } from '@/constants/event';
import { WORK_YEAR_LABELS } from '@/constants/material';
import useUserInfo from '@/hooks/use-user-info'; import useUserInfo from '@/hooks/use-user-info';
import { MaterialProfile } from '@/types/material'; import { MaterialProfile } from '@/types/material';
import { logWithPrefix } from '@/utils/common'; import { logWithPrefix } from '@/utils/common';
import { collectEvent, reportEvent } from '@/utils/event'; import { collectEvent, reportEvent } from '@/utils/event';
import { getBasicInfo, getSalary, requestProfileDetail, sortVideos } from '@/utils/material'; import { requestProfileDetail, sortVideos } from '@/utils/material';
import { navigateTo } from '@/utils/route'; import { navigateTo } from '@/utils/route';
import Toast from '@/utils/toast'; import Toast from '@/utils/toast';
import { isValidUserInfo } from '@/utils/user'; import { isValidUserInfo } from '@/utils/user';
@ -35,9 +34,8 @@ function MaterialCard(props: IProps) {
const userInfo = useUserInfo(); const userInfo = useUserInfo();
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [profile, setProfile] = useState<MaterialProfile | null>(null); const [profile, setProfile] = useState<MaterialProfile | null>(null);
const refreshRef = useRef((_f?: boolean) => {}); const refreshRef = useRef((_f?: boolean) => { });
const hasMaterial = !!profile; const hasMaterial = !!profile;
const firstCover = sortVideos(profile?.materialVideoInfoList || [])[0];
const handleGoCreateProfile = useCallback(() => { const handleGoCreateProfile = useCallback(() => {
reportEvent(ReportEventId.CLICK_GO_TO_CREATE_MATERIAL); reportEvent(ReportEventId.CLICK_GO_TO_CREATE_MATERIAL);
@ -46,7 +44,7 @@ function MaterialCard(props: IProps) {
const handleGoProfile = useCallback(() => { const handleGoProfile = useCallback(() => {
if (!hasMaterial) { if (!hasMaterial) {
realtimeLogger.info('handleGoProfile noMaterial'); realtimeLogger.info('handleGoProfile noMaterial')
return; return;
} }
navigateTo(PageUrl.MaterialProfile).catch(err => { navigateTo(PageUrl.MaterialProfile).catch(err => {
@ -69,7 +67,7 @@ function MaterialCard(props: IProps) {
try { try {
const profileDetail = await requestProfileDetail(); const profileDetail = await requestProfileDetail();
if (!profileDetail) { if (!profileDetail) {
realtimeLogger.info('getProfileDetail no profileDetail'); realtimeLogger.info('getProfileDetail no profileDetail')
} }
setProfile(profileDetail); setProfile(profileDetail);
} catch (e) { } catch (e) {
@ -92,10 +90,8 @@ function MaterialCard(props: IProps) {
refreshRef.current?.(true); refreshRef.current?.(true);
}; };
Taro.eventCenter.on(EventName.CREATE_PROFILE, callback); Taro.eventCenter.on(EventName.CREATE_PROFILE, callback);
Taro.eventCenter.on(EventName.UPDATE_PROFILE, callback);
return () => { return () => {
Taro.eventCenter.off(EventName.CREATE_PROFILE, callback); Taro.eventCenter.off(EventName.CREATE_PROFILE, callback);
Taro.eventCenter.on(EventName.UPDATE_PROFILE, callback);
}; };
}, [userInfo]); }, [userInfo]);
@ -104,11 +100,16 @@ function MaterialCard(props: IProps) {
<div className={`${PREFIX}__header`}> <div className={`${PREFIX}__header`}>
<div className={`${PREFIX}__header__left`}> <div className={`${PREFIX}__header__left`}>
<div className={`${PREFIX}__header__title`}></div> <div className={`${PREFIX}__header__title`}></div>
{/* {profile && (
<div
className={`${PREFIX}__header__progress`}
>{`完成度${Math.min((profile.progressBar || 0) * 100, 100)}%`}</div>
)} */}
</div> </div>
{profile && ( {profile && (
<div className={`${PREFIX}__header__right`}> <div className={`${PREFIX}__header__right`}>
<div></div> {/* <div className={`${PREFIX}__header__status`}>{profile?.isOpen ? '开放中' : '关闭'}</div> */}
<ArrowRight color="#6D3DF5" className={`${PREFIX}__header__icon`} /> <ArrowRight className={`${PREFIX}__header__icon`} />
</div> </div>
)} )}
</div> </div>
@ -122,21 +123,18 @@ function MaterialCard(props: IProps) {
</div> </div>
)} )}
{!loading && hasMaterial && ( {!loading && hasMaterial && (
<div className={`${PREFIX}__info`}> <ScrollView className={`${PREFIX}__scroll-view`} showScrollbar={false} enableFlex enhanced scrollX>
{firstCover && ( <div className={`${PREFIX}__cover-list`}>
<div className={`${PREFIX}__info-left`}> {sortVideos(profile?.materialVideoInfoList || []).map(video => (
<Image className={`${PREFIX}__info-cover`} mode="aspectFill" src={firstCover.coverUrl} /> <Image
</div> className={`${PREFIX}__cover-image`}
)} mode="aspectFit"
<div className={`${PREFIX}__info-right`}> key={video.coverUrl}
<div className="name">{profile?.name}</div> src={video.coverUrl}
<div className="info"> />
{WORK_YEAR_LABELS[profile?.workedYear] || ''}·{getBasicInfo({ ...profile, shoeSize: null })} ))}
</div>
<div className="worked"> {profile?.workedSecCategoryStr}</div>
<div className="salary">{getSalary(profile)}</div>
</div>
</div> </div>
</ScrollView>
)} )}
{loading && <Loading />} {loading && <Loading />}
</div> </div>

View File

@ -2,13 +2,13 @@ import { Image } from '@tarojs/components';
import classNames from 'classnames'; import classNames from 'classnames';
import { PropsWithChildren, useEffect, useState, useCallback } from 'react'; import { PropsWithChildren, useEffect, useState, useCallback } from 'react';
import { PageUrl } from '@/constants/app';
import { MaterialViewSource } from '@/constants/material'; import { MaterialViewSource } from '@/constants/material';
import useUserInfo from '@/hooks/use-user-info'; import useUserInfo from '@/hooks/use-user-info';
import { IChatMessage } from '@/types/message'; import { IChatMessage } from '@/types/message';
import { getScrollItemId } from '@/utils/common'; import { getScrollItemId } from '@/utils/common';
import { navigateTo } from '@/utils/route'; import { navigateTo } from '@/utils/route';
import { PageUrl } from '@/constants/app';
import './index.less'; import './index.less';
@ -19,13 +19,12 @@ export interface IBaseMessageProps {
export interface IUserMessageProps extends PropsWithChildren, IBaseMessageProps { export interface IUserMessageProps extends PropsWithChildren, IBaseMessageProps {
isRead?: boolean; isRead?: boolean;
resumeId?: string;
} }
const PREFIX = 'base-message'; const PREFIX = 'base-message';
function BaseMessage(props: IUserMessageProps) { function BaseMessage(props: IUserMessageProps) {
const { id, message, isRead: isReadProps, children, resumeId } = props; const { id, message, isRead: isReadProps, children } = props;
const { userId } = useUserInfo(); const { userId } = useUserInfo();
const [isRead, setIsRead] = useState(message.isRead); const [isRead, setIsRead] = useState(message.isRead);
const isSender = message.senderUserId === userId; const isSender = message.senderUserId === userId;
@ -38,12 +37,10 @@ function BaseMessage(props: IUserMessageProps) {
// const timer = setTimeout(() => setIsRead(true), 1200); // const timer = setTimeout(() => setIsRead(true), 1200);
// return () => clearTimeout(timer); // return () => clearTimeout(timer);
// }, [isSender]); // }, [isSender]);
const handleClick = useCallback(() => { const handleClick = useCallback(
if (!resumeId || isSender) { () => navigateTo(PageUrl.MaterialView, { resumeId: message.jobId, source: MaterialViewSource.Chat }),
return; [message.jobId]
} );
navigateTo(PageUrl.MaterialView, { resumeId: resumeId, source: MaterialViewSource.Chat });
}, [resumeId, isSender]);
useEffect(() => { useEffect(() => {
if (isRead) { if (isRead) {
return; return;
@ -56,7 +53,6 @@ function BaseMessage(props: IUserMessageProps) {
<Image <Image
mode="aspectFit" mode="aspectFit"
className={`${PREFIX}__avatar`} className={`${PREFIX}__avatar`}
onClick={handleClick}
src={message.senderAvatarUrl || require('@/statics/png/default_avatar.png')} src={message.senderAvatarUrl || require('@/statics/png/default_avatar.png')}
/> />
<div className={`${PREFIX}__content-container`}> <div className={`${PREFIX}__content-container`}>

View File

@ -80,8 +80,4 @@
margin-top: 24px; margin-top: 24px;
} }
} }
&__no-subscription {
}
} }

View File

@ -57,9 +57,7 @@ export function MessageNoTimesDialog(props: INoTimesProps) {
<Dialog className={NO_TIMES} onClose={onClose} open={open}> <Dialog className={NO_TIMES} onClose={onClose} open={open}>
<Dialog.Content> <Dialog.Content>
<div className={`${NO_TIMES}__title`}></div> <div className={`${NO_TIMES}__title`}></div>
<div className={`${NO_TIMES}__tips`}> <div className={`${NO_TIMES}__tips`}><span className="highlight"></span></div>
<span className="highlight"></span>
</div>
<div className={`${NO_TIMES}__body`}> <div className={`${NO_TIMES}__body`}>
<div className={`${NO_TIMES}__times`}>{`未读消息提醒剩余:${times}`}</div> <div className={`${NO_TIMES}__times`}>{`未读消息提醒剩余:${times}`}</div>
<Button className={`${NO_TIMES}__btn`} onClick={onClick}> <Button className={`${NO_TIMES}__btn`} onClick={onClick}>

View File

@ -12,7 +12,7 @@ import './index.less';
const PREFIX = 'partner-fragment-banner'; const PREFIX = 'partner-fragment-banner';
export default function PartnerBanner({ isBoss = false }) { export default function PartnerBanner() {
const userInfo = useUserInfo(); const userInfo = useUserInfo();
const needPhone = isNeedPhone(userInfo); const needPhone = isNeedPhone(userInfo);
const isPartner = userInfo.isPartner; const isPartner = userInfo.isPartner;
@ -50,11 +50,7 @@ export default function PartnerBanner({ isBoss = false }) {
<div className={PREFIX} onClick={handleClick}> <div className={PREFIX} onClick={handleClick}>
<Image <Image
className={`${PREFIX}__image`} className={`${PREFIX}__image`}
src={ src="https://publiccdn.neighbourhood.com.cn/img/partner_banner.png"
isBoss
? 'https://publiccdn.neighbourhood.com.cn/img/partner_banner.png'
: 'https://publiccdn.neighbourhood.com.cn/img/partner-banner-boss.png'
}
mode="scaleToFill" mode="scaleToFill"
/> />
{needPhone && ( {needPhone && (

View File

@ -2,53 +2,26 @@
@import '@/styles/common.less'; @import '@/styles/common.less';
.partner-intro { .partner-intro {
background: #eae5fb; padding-top: 112px;
padding-top: 271px; padding-bottom: calc(112px + constant(safe-area-inset-bottom));
position: relative; padding-bottom: calc(112px + env(safe-area-inset-bottom));
padding-bottom: calc(142px + constant(safe-area-inset-bottom));
padding-bottom: calc(142px + env(safe-area-inset-bottom));
&__bg { &__banner {
width: 100%; background: fade(@blHighlightBg, 8);
height: 705px; height: 88px;
position: absolute; line-height: 88px;
top: 0; text-align: center;
font-size: 28px;
position: fixed;
top: 98px;
left: 0; left: 0;
} right: 0;
&__slogan { width: 100vw;
top: 66px;
left: 44px;
position: absolute;
font-family: 'Alimama ShuHeiTi';
font-style: normal;
font-weight: 700;
font-size: 44px;
line-height: 62px;
color: #333333;
> div {
margin-bottom: 12px;
}
&-highlight {
display: inline-block;
padding-left: 12px;
padding-right: 9px;
font-size: 64px;
line-height: 62px;
background: linear-gradient(87.53deg, #683de3 0.4%, #39227d 84.55%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
} }
&__main { &__main {
padding-left: 24px; padding-left: 24px;
padding-right: 24px; padding-right: 24px;
position: relative;
z-index: 1;
} }
&__highlight { &__highlight {
@ -61,90 +34,14 @@
} }
&__block { &__block {
margin-bottom: 12px; margin-bottom: 40px;
position: relative;
} }
&__swiper {
&-wrapper {
margin-bottom: 8px;
background: #fff;
border-radius: 24px;
position: relative;
border: 1px solid #6d3df5;
}
&-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
&__swiper-item {
box-sizing: border-box;
height: 130px;
padding: 24px 32px;
&-top-line {
.flex-row();
font-style: normal;
font-size: 28px;
line-height: 40px;
color: #333333;
font-weight: 400;
position: relative;
.noWrap();
}
&-id {
.id {
color: #999999;
display: inline-block;
padding-left: 10px;
}
}
&-time {
padding-right: 55px;
}
&-details {
margin-top: 5px;
.flex-row();
justify-content: space-between;
}
&-tag {
height: 36px;
margin-right: 16px;
padding: 0 12px;
border: 1px solid #6d3df5;
border-radius: 4px;
font-size: 24px;
line-height: 36px;
color: #6d3df5;
display: inline-block;
}
&-info {
font-size: 28px;
line-height: 40px;
color: #333333;
display: inline-block;
margin-right: 16px;
&:last-child {
margin-right: 0;
}
.money {
color: #ff5051;
display: inline-block;
padding-left: 8px;
}
}
}
&__card { &__card {
background: #fff; background: #fff;
border-radius: 24px; border-radius: 24px;
padding: 68px 32px 44px; padding: 24px 32px;
} }
&__h1 { &__h1 {
@ -154,81 +51,33 @@
color: #1d2129; color: #1d2129;
margin-bottom: 16px; margin-bottom: 16px;
margin-top: 32px; margin-top: 32px;
position: relative;
z-index: 0;
&.no-dot {
&:after {
background: transparent;
}
}
&:first-child { &:first-child {
margin-top: 0; margin-top: 0;
} }
&:after {
content: '';
width: 28px;
height: 28px;
background: #ffd236;
border-radius: 50%;
position: absolute;
left: -6px;
top: -3px;
z-index: -1;
}
} }
&__body { &__body {
font-weight: 400; font-weight: 400;
font-size: 28px; font-size: 28px;
line-height: 40px; line-height: 40px;
color: @blColor;
&.grey { &.grey {
color: @blColorG2; color: @blColorG2
}
&.center {
text-align: center;
} }
} }
&__title { &__title {
margin-left: 48px; margin-bottom: 24px;
margin-right: 48px;
font-weight: 500; font-weight: 500;
font-size: 32px; font-size: 32px;
line-height: 80px; line-height: 32px;
height: 80px;
transform: translateY(50%);
background: #ffd236;
box-shadow: 0px 12px 0px #ffa35d;
border-radius: 60px;
color: #1d2129;
text-align: center;
position: relative;
}
&__recommend { color: #1d2129;
display: inline-flex;
line-height: 36px;
padding: 0 8px;
height: 36px;
background: rgb(255, 80, 81);
border-radius: 4px;
font-size: 24px;
color: #fff;
align-items: center;
position: absolute;
gap: 6px;
right: -25px;
top: -18px;
} }
&__special { &__special {
padding-top: 75px; padding: 32px;
.flex-column(); .flex-column();
.partner-intro__body { .partner-intro__body {
@ -251,7 +100,6 @@
&__footer { &__footer {
position: fixed; position: fixed;
bottom: 0; bottom: 0;
z-index: 2;
width: 100vw; width: 100vw;
background: #ffffff; background: #ffffff;
padding: 12px 32px; padding: 12px 32px;
@ -264,20 +112,17 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
box-sizing: border-box; box-sizing: border-box;
justify-content: center;
} }
&__download-button { &__download-button {
//flex: 1 1; flex: 1 1;
.button(@height: 88px; @fontSize: 32px; @fontWeight: 500; @borderRadius: 44px; @highlight: 0); .button(@height: 88px; @fontSize: 32px; @fontWeight: 500; @borderRadius: 44px; @highlight: 0);
} }
&__share-button { &__share-button {
//flex: 2 2; flex: 2 2;
flex: 0 0 406px;
width: 406px;
.button(@height: 88px; @fontSize: 32px; @fontWeight: 500; @borderRadius: 44px;); .button(@height: 88px; @fontSize: 32px; @fontWeight: 500; @borderRadius: 44px;);
//margin-left: 32px; margin-left: 32px;
} }
} }

View File

@ -1,220 +1,174 @@
import { Button, Canvas, Image } from '@tarojs/components'; import { Button, Canvas } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { Swiper } from '@taroify/core'; import { useCallback, useState } from 'react';
import { GoodJob } from '@taroify/icons';
import { useCallback, useEffect, useRef, useState } from 'react';
import LoginDialog from '@/components/login-dialog';
import { PageUrl } from '@/constants/app';
import useUserInfo from '@/hooks/use-user-info';
import { EarnType, UserProfitListItem } from '@/types/partner';
import { openCustomerServiceChat } from '@/utils/common'; import { openCustomerServiceChat } from '@/utils/common';
import { becomePartner, formatMoney, formatTimestamp, formatUserId, getLastProfitList } from '@/utils/partner'; import { getPartnerQrcode } from '@/utils/partner';
import { navigateTo } from '@/utils/route';
import { isNeedPhone, requestUserInfo } from '@/utils/user';
import './index.less'; import './index.less';
const PREFIX = 'partner-intro'; const PREFIX = 'partner-intro';
export default function PartnerIntro() { export default function PartnerIntro() {
const userInfo = useUserInfo(); const [posterPath, setPosterPath] = useState('');
const needPhone = isNeedPhone(userInfo); const getQrcode = async () => {
const [loginVisible, setLoginVisible] = useState(false); try {
const data = await getPartnerQrcode(); // 假设 getPartnerQrcode 返回 ArrayBuffer
const base64 = Taro.arrayBufferToBase64(data);
return `data:image/png;base64,${base64}`;
} catch (error) {
console.error('获取二维码失败', error);
Taro.showToast({ title: '获取二维码失败', icon: 'none' });
throw error;
}
};
const saveCanvasToTempFile = (): Promise<string> => {
return new Promise((resolve, reject) => {
try {
const query = Taro.createSelectorQuery().select('#posterCanvas');
query.fields({ node: true }).exec(async res => {
const canvas = res[0].node;
const tempFilePath = await Taro.canvasToTempFilePath({
canvas,
x: 0,
y: 0,
width: 1500, // 实际绘制宽度
height: 2668, // 实际绘制高度
destWidth: 750, // 目标显示宽度
destHeight: 1334, // 目标显示高度
fileType: 'jpg',
});
setPosterPath(tempFilePath.tempFilePath);
resolve(tempFilePath.tempFilePath);
});
} catch (error) {
console.error('保存 Canvas 到临时文件失败', error);
Taro.showToast({ title: '保存 Canvas 失败', icon: 'none' });
reject(error);
}
});
};
const drawCanvas = (qrCode: string): Promise<string> => {
const query = Taro.createSelectorQuery().select('#posterCanvas');
return new Promise(resolve => {
query.fields({ node: true, size: true }).exec(async res => {
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
canvas.width = 1500;
canvas.height = 2668;
ctx.scale(2, 2);
// 绘制背景图片
const bgImage = canvas.createImage();
const poster = 'https://publiccdn.neighbourhood.com.cn/img/poster.png'
bgImage.src = poster;
bgImage.onload = () => {
ctx.drawImage(bgImage, 0, 0, 750, 1334);
const qrCodeImage = canvas.createImage();
qrCodeImage.src = qrCode; // 假设 getQrcode() 返回的是二维码图片的路径
qrCodeImage.onload = () => {
ctx.drawImage(qrCodeImage, 235, 894, 280, 280); // 绘制二维码,位置和大小
saveCanvasToTempFile().then(tempPath => {
resolve(tempPath);
});
};
};
bgImage.onerror = err => {
console.error(err);
};
});
});
};
const savePoster = async () => {
let filePath = posterPath;
if (!filePath) {
Taro.showLoading({ title: '正在生成海报' });
const qrCode = await getQrcode();
filePath = await drawCanvas(qrCode);
Taro.hideLoading();
}
const res = await Taro.getSetting();
const hasPermission = res.authSetting['scope.writePhotosAlbum'];
if (hasPermission === false) {
Taro.showModal({
title: '提示',
content: '需要访问相册权限才能保存图片,请前往设置开启权限',
showCancel: false,
success() {
Taro.openSetting();
},
});
} else {
try {
await Taro.authorize({ scope: 'scope.writePhotosAlbum' });
await Taro.saveImageToPhotosAlbum({ filePath });
Taro.showToast({ title: '保存成功', icon: 'success' });
} catch (error) {
console.error(error);
Taro.showToast({ title: '保存失败', icon: 'none' });
}
}
};
const handleOpenService = useCallback(() => { const handleOpenService = useCallback(() => {
openCustomerServiceChat('https://work.weixin.qq.com/kfid/kfc4fcf6b109b3771d7'); openCustomerServiceChat('https://work.weixin.qq.com/kfid/kfc4fcf6b109b3771d7');
}, []); }, []);
const handleBindSuccess = useCallback(async () => {
await becomePartner();
await requestUserInfo();
setLoginVisible(false);
}, []);
const handleBecomePartner = useCallback(() => {
if (needPhone) {
setLoginVisible(true);
return false;
} else {
handleBindSuccess();
return true;
}
}, [handleBindSuccess, needPhone]);
const handleConfirm = useCallback(() => {
if (!userInfo.isPartner && !handleBecomePartner()) {
return;
}
navigateTo(PageUrl.GroupOwnerCertificate);
}, [handleBecomePartner, userInfo.isPartner]);
const handleJump = useCallback(() => {
if (!userInfo.isPartner && !handleBecomePartner()) {
return;
}
navigateTo(PageUrl.PartnerShareVip);
}, [handleBecomePartner, userInfo.isPartner]);
const timerRef = useRef<NodeJS.Timeout | null>(null);
const [bannerList, setBannerList] = useState<UserProfitListItem[]>([]);
const getBannerList = useCallback(async () => {
if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
const list = await getLastProfitList();
setBannerList(list);
timerRef.current = setTimeout(
() => {
getBannerList();
},
3000 * (list.length || 10)
);
}, []);
useEffect(() => {
getBannerList();
return () => {
if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
};
}, [getBannerList]);
const showedRef = useRef(false);
useEffect(() => {
if (showedRef.current || !userInfo.userId) {
return;
}
showedRef.current = true;
if (!userInfo.isPartner) {
handleBecomePartner();
}
}, [userInfo]);
return ( return (
<div className={PREFIX}> <div className={PREFIX}>
<Image <div className={`${PREFIX}__banner`}>
src="https://publiccdn.neighbourhood.com.cn/img/partner-intro-bg.png" <span className={`${PREFIX}__highlight`}>75%</span>
className={`${PREFIX}__bg`}
mode="aspectFill"
/>
<div className={`${PREFIX}__slogan`}>
<div></div>
<div>
<div className={`${PREFIX}__slogan-highlight`}>75%</div>
</div>
</div> </div>
<div className={`${PREFIX}__main`}> <div className={`${PREFIX}__main`}>
<div className={`${PREFIX}__swiper-wrapper`}>
<Image
className={`${PREFIX}__swiper-bg`}
src="https://publiccdn.neighbourhood.com.cn/img/partner-swipe-item.png"
mode="aspectFill"
/>
<Swiper className={`${PREFIX}__swiper`} autoplay={3000} touchable={false}>
{bannerList.map((item, index) => (
<Swiper.Item className={`${PREFIX}__swiper-item`} key={`${item.userId}-${index}`}>
<div className={`${PREFIX}__swiper-item-top-line`}>
<div className={`${PREFIX}__swiper-item-time`}>{formatTimestamp(item.updatedAt)}</div>
<div className={`${PREFIX}__swiper-item-id`}>
<div className="id">{formatUserId(item.userId)}</div>
</div>
</div>
<div className={`${PREFIX}__swiper-item-details`}>
<div>
<div className={`${PREFIX}__swiper-item-tag`}></div>
<div className={`${PREFIX}__swiper-item-info`}>
{[EarnType.CHAT_ACTIVITY_SHARE_L1, EarnType.CHAT_ACTIVITY_SHARE_L2].includes(item.earnType)
? '主播被开聊'
: '会员支付'}
<div className="money">+{formatMoney(item.amount)}</div>
</div>
</div>
<div className={`${PREFIX}__swiper-item-info`}>
<div className="money">{formatMoney(item.total)}</div>
</div>
</div>
</Swiper.Item>
))}
</Swiper>
</div>
<div className={`${PREFIX}__block`}> <div className={`${PREFIX}__block`}>
<div className={`${PREFIX}__title`}>3</div>
<div className={`${PREFIX}__card`}> <div className={`${PREFIX}__card`}>
<div className={`${PREFIX}__h1`}></div> <div className={`${PREFIX}__h1`}></div>
<div className={`${PREFIX}__body`}> <div className={`${PREFIX}__body`}>
<span className={`${PREFIX}__highlight`}>20%</span> <span className={`${PREFIX}__highlight`}>20%</span>
</div> </div>
<div className={`${PREFIX}__h1`}></div> <div className={`${PREFIX}__h1`}></div>
<div className={`${PREFIX}__body`}> <div className={`${PREFIX}__body`}>
<span className={`${PREFIX}__highlight`}>50%</span> <span className={`${PREFIX}__highlight`}>50%</span>
</div> </div>
<div className={`${PREFIX}__h1`}></div> <div className={`${PREFIX}__h1`}></div>
<div className={`${PREFIX}__body`}> <div className={`${PREFIX}__body`}>
<span className={`${PREFIX}__highlight`}>5%</span> <span className={`${PREFIX}__highlight`}>5%</span>
</div> </div>
</div> </div>
</div> </div>
<div className={`${PREFIX}__block`}> <div className={`${PREFIX}__block`}>
<div className={`${PREFIX}__title`}></div> <div className={`${PREFIX}__title`}></div>
<div className={`${PREFIX}__card`}> <div className={`${PREFIX}__card`}>
<div className={`${PREFIX}__body`}> <div className={`${PREFIX}__body`}></div>
<div className={`${PREFIX}__h1`}></div>
<div></div>
<div className={`${PREFIX}__h1`}></div>
<div></div>
</div>
</div>
</div>
<div className={`${PREFIX}__block`}>
<div className={`${PREFIX}__title`}>
<div className={`${PREFIX}__recommend`}>
<GoodJob />
</div>
</div>
<div className={`${PREFIX}__card ${PREFIX}__special`}>
<div className={`${PREFIX}__body`}>
<div className="center">
访
</div>
</div>
<Button className={`${PREFIX}__service`} onClick={handleConfirm}>
</Button>
</div> </div>
</div> </div>
<div className={`${PREFIX}__block`}> <div className={`${PREFIX}__block`}>
<div className={`${PREFIX}__title`}></div> <div className={`${PREFIX}__title`}></div>
<div className={`${PREFIX}__card ${PREFIX}__special`}> <div className={`${PREFIX}__card ${PREFIX}__special`}>
<div className={`${PREFIX}__h1 no-dot`}></div> <div className={`${PREFIX}__h1`}></div>
<div className={`${PREFIX}__body grey`}></div>
<Button className={`${PREFIX}__service`} onClick={handleOpenService}> <Button className={`${PREFIX}__service`} onClick={handleOpenService}>
</Button> </Button>
</div> </div>
<div className={`${PREFIX}__tip`}></div>
</div> </div>
</div> </div>
<Canvas id="posterCanvas" canvas-id="posterCanvas" type="2d" style="width: 750px; height: 1334px;" /> <Canvas id="posterCanvas" canvas-id="posterCanvas" type="2d" style="width: 750px; height: 1334px;" />
<div className={`${PREFIX}__footer`}> <div className={`${PREFIX}__footer`}>
<Button className={`${PREFIX}__share-button`} onClick={handleJump}> <Button className={`${PREFIX}__download-button`} onClick={savePoster}>
</Button>
<Button className={`${PREFIX}__share-button`} openType="share">
</Button> </Button>
</div> </div>
{loginVisible && (
<LoginDialog onCancel={() => setLoginVisible(false)} onSuccess={handleBindSuccess} needPhone={needPhone} />
)}
</div> </div>
); );
} }

View File

@ -125,7 +125,7 @@ function PartnerList(props: {
hasMore={hasMore} hasMore={hasMore}
onLoad={handleLoadMore} onLoad={handleLoadMore}
loading={loadingMore || refreshing} loading={loadingMore || refreshing}
disabled={loadMoreError || !visible} disabled={loadMoreError}
fixedHeight={typeof listHeight !== 'undefined'} fixedHeight={typeof listHeight !== 'undefined'}
style={listHeight ? { height: `${listHeight}px` } : undefined} style={listHeight ? { height: `${listHeight}px` } : undefined}
> >

View File

@ -8,51 +8,7 @@
background: #6d3df5; background: #6d3df5;
color: #fff; color: #fff;
&-simple { &__simple {
padding: 39px 35px;
height: 120px;
box-sizing: border-box;
&__bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
border-radius: 16px;
height: 100%;
}
&__content {
.flex-row();
line-height: 42px;
position: relative;
z-index: 1;
}
&__title {
font-size: 26px;
color: #FFFFFF;
}
&__money {
font-weight: 800;
margin-left: 12px;
font-size: 44px;
}
&__button {
position: absolute;
z-index: 2;
top: 44px;
right: 24px;
font-size: 28px;
line-height: 32px;
&-image {
width: 24px;
height: 24px;
display: inline-block;
margin-left: 8px;
}
}
} }
&__bg { &__bg {
@ -72,7 +28,20 @@
z-index: 1; z-index: 1;
} }
&__button {
position: absolute;
z-index: 2;
top: 44px;
right: 56px;
font-size: 24px;
line-height: 24px;
&-image {
width: 20px;
height: 20px;
display: inline-block;
margin-left: 4px;
}
}
&__title { &__title {
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;

View File

@ -1,16 +1,14 @@
import { Button, Image } from '@tarojs/components'; import { Button, Image } from '@tarojs/components';
import Taro, { useDidShow } from '@tarojs/taro';
import { Dialog } from '@taroify/core'; import { Dialog } from '@taroify/core';
import { Question } from '@taroify/icons'; import { Question } from '@taroify/icons';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useState, useEffect } from 'react';
import { PageUrl } from '@/constants/app'; import { PageUrl } from '@/constants/app';
import { PartnerProfitsState } from '@/types/partner'; import { PartnerProfitsState } from '@/types/partner';
import { formatMoney, getPartnerProfitStat, withdrawMoney } from '@/utils/partner'; import { formatMoney, getPartnerProfitStat } from '@/utils/partner';
import { navigateTo } from '@/utils/route'; import { navigateTo } from '@/utils/route';
import Toast from '@/utils/toast'; import Toast from '@/utils/toast';
import './index.less'; import './index.less';
const PREFIX = 'partner-kanban'; const PREFIX = 'partner-kanban';
@ -33,48 +31,17 @@ function TipDialog(props: { open: boolean; onClose: () => void }) {
} }
function WithdrawDialog(props: { open: boolean; onClose: () => void; count: number }) { function WithdrawDialog(props: { open: boolean; onClose: () => void; count: number }) {
const handleWithdraw = useCallback(async () => { const handleWithdraw = useCallback(() => {}, []);
if (Taro.canIUse('requestMerchantTransfer')) {
try {
const result = await withdrawMoney();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
wx.requestMerchantTransfer({
mchId: '1642470088',
appId: 'wxf0724a83f8e377d2',
package: result.packageInfo,
success: (res: never) => {
// res.err_msg将在页面展示成功后返回应用时返回ok并不代表付款成功
console.log('success:', res);
Toast.success('提现成功');
props.onClose();
},
fail: (res: never) => {
Toast.error('提现失败');
console.log('fail:', res);
},
});
} catch (e) {
Toast.error('提现订单创建失败');
console.log(e);
}
} else {
await Taro.showModal({
content: '你的微信版本过低,请更新至最新版本。',
showCancel: false,
});
}
}, [props]);
return ( return (
<Dialog open={props.open} onClose={props.onClose}> <Dialog open={props.open} onClose={props.onClose}>
<Dialog.Content> <Dialog.Content>
<div className={`${PREFIX}-withdraw-dialog__container`}> <div className={`${PREFIX}-withdraw-dialog__container`}>
<div className={`${PREFIX}-withdraw-dialog__title`}></div> <div className={`${PREFIX}-withdraw-dialog__title`}></div>
<div className={`${PREFIX}-withdraw-dialog__count`}> <div className={`${PREFIX}-withdraw-dialog__count`}>
{+props.count} {props.count}
<div className="yuan"></div> <div className="yuan"></div>
</div> </div>
<div className={`${PREFIX}-withdraw-dialog__hint`}>200</div> <div className={`${PREFIX}-withdraw-dialog__hint`}>500</div>
<Button className={`${PREFIX}-withdraw-dialog__confirm-button`} onClick={handleWithdraw}> <Button className={`${PREFIX}-withdraw-dialog__confirm-button`} onClick={handleWithdraw}>
</Button> </Button>
@ -119,62 +86,41 @@ export default function PartnerKanban({ simple }: PartnerKanbanProps) {
const handleTipClose = useCallback(() => { const handleTipClose = useCallback(() => {
setTipOpen(false); setTipOpen(false);
}, []); }, []);
const handleViewWithdraw = useCallback(() => {
if (stats.availableBalance < 10 * 1000) {
Toast.info('提现金额需大于等于10元');
return;
}
setWithdrawOpen(true);
}, []);
const handleWithdrawClose = useCallback(() => {
setWithdrawOpen(false);
}, []);
const getProfitStats = useCallback(async () => { const getProfitStats = useCallback(async () => {
const data = await getPartnerProfitStat(); const data = await getPartnerProfitStat();
setStats(data); setStats(data);
}, []); }, []);
const handleViewWithdraw = useCallback(() => {
if (stats.availableBalance < 0) {
Toast.info('提现金额需大于等于0元');
return;
}
// if (stats.availableBalance < 10 * 1000) {
// Toast.error('提现金额需大于等于10元');
// return;
// }
setWithdrawOpen(true);
}, [stats.availableBalance]);
const handleWithdrawClose = useCallback(() => {
setWithdrawOpen(false);
getProfitStats();
}, [getProfitStats]);
useDidShow(() => {
getProfitStats();
});
useEffect(() => { useEffect(() => {
getProfitStats(); getProfitStats();
}, []); }, []);
if (simple) {
return ( return (
<div className={`${PREFIX} ${PREFIX}-simple`}> <div className={`${PREFIX} ${simple ? `${PREFIX}__simple` : ''}`}>
<Image
className={`${PREFIX}-simple__bg`}
src="https://publiccdn.neighbourhood.com.cn/img/partner_bg.png"
mode="aspectFill"
/>
<div className={`${PREFIX}-simple__content`}>
<div className={`${PREFIX}-simple__title`}>()</div>
<div className={`${PREFIX}-simple__money`}>{formatMoney(total)}</div>
</div>
<div className={`${PREFIX}-simple__button`} onClick={handleNavigate}>
<Image
className={`${PREFIX}-simple__button-image`}
mode="aspectFit"
src={require('@/statics/svg/caret-right.svg')}
/>
</div>
</div>
);
}
return (
<div className={PREFIX}>
<Image <Image
className={`${PREFIX}__bg`} className={`${PREFIX}__bg`}
src="https://publiccdn.neighbourhood.com.cn/img/partner_bg.png" src="https://publiccdn.neighbourhood.com.cn/img/partner_bg.png"
mode="aspectFill" mode="aspectFill"
/> />
<div className={`${PREFIX}__content`}> <div className={`${PREFIX}__content`}>
{simple && (
<div className={`${PREFIX}__button`} onClick={handleNavigate}>
<Image
className={`${PREFIX}__button-image`}
mode="aspectFit"
src={require('@/statics/svg/caret-right.svg')}
/>
</div>
)}
<div className={`${PREFIX}__total`}> <div className={`${PREFIX}__total`}>
<div className={`${PREFIX}__title`}></div> <div className={`${PREFIX}__title`}></div>
<div className={`${PREFIX}__money`}>{formatMoney(total)}</div> <div className={`${PREFIX}__money`}>{formatMoney(total)}</div>
@ -196,6 +142,7 @@ export default function PartnerKanban({ simple }: PartnerKanbanProps) {
<div className={`${PREFIX}__money`}>{formatMoney(stats.availableProfit)}</div> <div className={`${PREFIX}__money`}>{formatMoney(stats.availableProfit)}</div>
</div> </div>
</div> </div>
{!simple && (
<div className={`${PREFIX}__buttons`}> <div className={`${PREFIX}__buttons`}>
<Button className={`${PREFIX}__withdraw`} onClick={handleViewWithdraw}> <Button className={`${PREFIX}__withdraw`} onClick={handleViewWithdraw}>
@ -204,13 +151,10 @@ export default function PartnerKanban({ simple }: PartnerKanbanProps) {
</Button> </Button>
</div> </div>
)}
</div> </div>
<TipDialog open={tipOpen} onClose={handleTipClose} /> {!simple && <TipDialog open={tipOpen} onClose={handleTipClose} />}
<WithdrawDialog {!simple && <WithdrawDialog count={350} open={withdrawOpen} onClose={handleWithdrawClose} />}
count={Math.min(Number(formatMoney(stats.availableBalance)), 200)}
open={withdrawOpen}
onClose={handleWithdrawClose}
/>
</div> </div>
); );
} }

View File

@ -96,8 +96,7 @@ function ProfitList(props: IPartnerProfitListProps) {
style={listHeight ? { height: `${listHeight}px` } : undefined} style={listHeight ? { height: `${listHeight}px` } : undefined}
> >
{dataList.map(item => { {dataList.map(item => {
const isChat = const isChat = type === ProfitType.CHAT_SHARE || item.earnType.toString().toLowerCase().indexOf('chat') > -1;
type === ProfitType.CHAT_SHARE || item.earnType.toString().toLowerCase().indexOf('chat') > -1;
return ( return (
<div className={`${PREFIX}__row`} key={item.id}> <div className={`${PREFIX}__row`} key={item.id}>
<div className={`${PREFIX}__row-content`}> <div className={`${PREFIX}__row-content`}>

View File

@ -78,12 +78,9 @@
&:last-child { &:last-child {
padding-right: 0; padding-right: 0;
} }
&.time { &.time,
flex: 2;
}
&.project { &.project {
width: 150px; flex: 2;
flex-shrink: 0;
} }
&.status { &.status {
width: 96px; width: 96px;

View File

@ -19,7 +19,7 @@ export interface IPhoneButtonProps extends ButtonProps {
message?: string; message?: string;
// 绑定后是否需要刷新接口获取手机号 // 绑定后是否需要刷新接口获取手机号
needPhone?: boolean; needPhone?: boolean;
onBindPhone?: (status: BindPhoneStatus, e: ITouchEvent) => void; onBindPhone?: (status: BindPhoneStatus) => void;
onSuccess?: (e: ITouchEvent) => void; onSuccess?: (e: ITouchEvent) => void;
} }
@ -44,17 +44,17 @@ export default function PhoneButton(props: IPhoneButtonProps) {
const encryptedData = e.detail.encryptedData; const encryptedData = e.detail.encryptedData;
const iv = e.detail.iv; const iv = e.detail.iv;
if (!encryptedData || !iv) { if (!encryptedData || !iv) {
onBindPhone?.(BindPhoneStatus.Cancel, e as ITouchEvent); onBindPhone?.(BindPhoneStatus.Cancel);
return Toast.error('取消授权'); return Toast.error('取消授权');
} }
try { try {
await setPhoneNumber({ encryptedData, iv }); await setPhoneNumber({ encryptedData, iv });
needPhone && (await requestUserInfo()); needPhone && (await requestUserInfo());
Toast.success(message); Toast.success(message);
onBindPhone?.(BindPhoneStatus.Success, e as ITouchEvent); onBindPhone?.(BindPhoneStatus.Success);
onSuccess?.(e as ITouchEvent); onSuccess?.(e as ITouchEvent);
} catch (err) { } catch (err) {
onBindPhone?.(BindPhoneStatus.Error, e as ITouchEvent); onBindPhone?.(BindPhoneStatus.Error);
Toast.error('绑定失败'); Toast.error('绑定失败');
log('bind phone fail', err); log('bind phone fail', err);
} }

View File

@ -1,87 +0,0 @@
@import '@/styles/common.less';
@import '@/styles/variables.less';
.prejob-popup {
&__content {
padding: 40px 32px;
}
&__title {
font-weight: 500;
font-size: 32px;
line-height: 48px;
margin-bottom: 31px;
text-align: center;
.highlight {
color: @blHighlightColor;
display: inline;
}
}
&__body {
}
&__item {
.flex-row();
margin-bottom: 40px;
&:last-child {
margin-bottom: 0;
}
&-icon {
width: 88px;
height: 88px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
&.material {
background: #feba00;
> image {
width: 40px;
height: 46px;
}
}
&.vip {
background: #b094ff;
> image {
width: 48px;
height: 42px;
}
}
&.video {
background: #34a853;
> image {
width: 46px;
height: 44px;
}
}
}
&-main {
padding: 0 24px;
flex: 1;
.title {
font-weight: 500;
font-size: 28px;
line-height: 40px;
margin-bottom: 8px;
}
.desc {
font-size: 24px;
line-height: 36px;
color: @blColorG1;
}
}
&-action {
flex: 0 0 auto;
}
}
&__btn {
.button(@width: 136px, @height: 72px, @fontSize: 28px, @fontWeight: 400, @borderRadius: 43px, @highlight: 0);
background: #f2f2f2;
}
}

View File

@ -1,137 +0,0 @@
import { Button, Image } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { Dialog, Popup } from '@taroify/core';
import { Fragment, useCallback, useState } from 'react';
import LoginButton from '@/components/login-button';
import JobBuy from '@/components/product-dialog/steps-ui/job-buy';
import SafeBottomPadding from '@/components/safe-bottom-padding';
import { PageUrl } from '@/constants/app';
import { CacheKey } from '@/constants/cache-key';
import { GET_CONTACT_TYPE } from '@/constants/job';
import { navigateTo, switchTab } from '@/utils/route';
import './index.less';
interface IProps {
onCancel: () => void;
onRefresh: () => void;
needLogin?: boolean;
isCreateResume?: boolean;
onConfirm: (type: GET_CONTACT_TYPE) => void;
}
const PREFIX = 'prejob-popup';
const GET_CONTACT_TYPE_OPTIONS = [
{
type: GET_CONTACT_TYPE.MATERIAL,
icon: 'https://publiccdn.neighbourhood.com.cn/img/file.svg',
title: '创建模卡(送会员)',
desc: '送日会员,优先推荐给企业,机会更多',
btnText: '创建',
needLogin: true,
},
{
type: GET_CONTACT_TYPE.VIP,
icon: 'https://publiccdn.neighbourhood.com.cn/img/diamond.svg',
title: '播络会员',
desc: '开通会员每天免费查看',
btnText: '开通',
},
// {
// type: GET_CONTACT_TYPE.GROUP,
// icon: 'https://publiccdn.neighbourhood.com.cn/img/group-avatar.png',
// title: '进群领会员(免费报单)',
// desc: '群内定期发放会员,免费报单',
// btnText: '进群',
// },
{
type: GET_CONTACT_TYPE.INVITE,
icon: 'https://publiccdn.neighbourhood.com.cn/img/invite-operations.png',
title: '邀请运营进群(送会员)',
desc: '每邀请进一个主播群送一个日会员',
btnText: '邀请',
},
];
export function PrejobPopup({ onCancel, isCreateResume, onConfirm, onRefresh }: IProps) {
const [openPopup, setOpenPopup] = useState(true);
const [openDialog, setOpenDialog] = useState(false);
const [clicked, setClicked] = useState(!!Taro.getStorageSync(CacheKey.JOIN_GROUP_POPUP_CLICKED));
const handleClick = (type: GET_CONTACT_TYPE) => () => {
if (type === GET_CONTACT_TYPE.MATERIAL) {
navigateTo(PageUrl.MaterialUploadVideo);
onConfirm(type);
}
if (type === GET_CONTACT_TYPE.INVITE) {
navigateTo(PageUrl.InviteOperations);
onConfirm(type);
}
if (type === GET_CONTACT_TYPE.VIP) {
setOpenPopup(false);
setOpenDialog(true);
}
if (type === GET_CONTACT_TYPE.GROUP) {
Taro.setStorageSync(CacheKey.JOIN_GROUP_POPUP_CLICKED, true);
setClicked(true);
switchTab(PageUrl.GroupV2);
onConfirm(type);
}
};
const handleAfterBuy = useCallback(async () => {
onConfirm(GET_CONTACT_TYPE.VIP);
}, [onConfirm]);
return (
<Fragment>
<Popup rounded className={PREFIX} placement="bottom" open={openPopup} onClose={onCancel}>
<div className={`${PREFIX}__content`}>
<div className={`${PREFIX}__title`}>
<div className="highlight"></div><div className="highlight"></div>
</div>
<div className={`${PREFIX}__body`}>
{GET_CONTACT_TYPE_OPTIONS.map(option => {
if (clicked && option.type === GET_CONTACT_TYPE.GROUP) {
return;
}
return (
<div className={`${PREFIX}__item`} key={option.type}>
<div className={`${PREFIX}__item-icon ${option.type}`}>
<Image mode="aspectFit" src={option.icon} />
</div>
<div className={`${PREFIX}__item-main`}>
<div className="title">{option.title}</div>
<div className="desc">{option.desc}</div>
</div>
<div className={`${PREFIX}__item-action`}>
{option.needLogin ? (
<LoginButton
needRefresh
onRefresh={onRefresh}
className={`${PREFIX}__btn`}
onClick={handleClick(option.type)}
>
{option.btnText}
</LoginButton>
) : (
<Button className={`${PREFIX}__btn`} onClick={handleClick(option.type)}>
{option.btnText}
</Button>
)}
</div>
</div>
);
})}
</div>
</div>
<SafeBottomPadding />
</Popup>
<Dialog open={openDialog} onClose={onCancel}>
<Dialog.Content>
<JobBuy onConfirm={handleAfterBuy} isCreateResume={isCreateResume} />
</Dialog.Content>
</Dialog>
</Fragment>
);
}

View File

@ -3,7 +3,6 @@ export const PREFIX = 'product-dialog';
export enum DialogStatus { export enum DialogStatus {
// 加载中 // 加载中
LOADING = 'loading', LOADING = 'loading',
PRE_ACTION = 'pre_action',
// 直接联系通告主 // 直接联系通告主
JOB_CONTACT_DIRECT = 'job_contact_direct', JOB_CONTACT_DIRECT = 'job_contact_direct',
// 联系客服去联系通告主 -> 订阅通知 // 联系客服去联系通告主 -> 订阅通知

View File

@ -82,10 +82,7 @@ function ProductGroupDialog(props: IProps) {
return; return;
} }
// 否则:如果有解锁次数,显示是否确定消费。无解锁次数,显示不无次数 UI // 否则:如果有解锁次数,显示是否确定消费。无解锁次数,显示不无次数 UI
const [[time], detail] = await Promise.all([ const [time, detail] = await Promise.all([requestProductBalance(PRODUCT_CODE), requestGroupDetail(blGroupId)]);
requestProductBalance(PRODUCT_CODE),
requestGroupDetail(blGroupId),
]);
setGroupDetail(detail); setGroupDetail(detail);
if (time <= 0) { if (time <= 0) {
setStatus(DialogStatus.GROUP_NEED_BUY_ADD); setStatus(DialogStatus.GROUP_NEED_BUY_ADD);

View File

@ -2,6 +2,7 @@
@import '@/styles/variables.less'; @import '@/styles/variables.less';
.product-dialog { .product-dialog {
.layout() { .layout() {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -30,7 +31,7 @@
font-weight: 500; font-weight: 500;
line-height: 72px; line-height: 72px;
border-radius: 44px; border-radius: 44px;
color: #ffffff; color: #FFFFFF;
background: @blHighlightColor; background: @blHighlightColor;
margin-top: 40px; margin-top: 40px;
} }
@ -41,7 +42,7 @@
font-size: 24px; font-size: 24px;
line-height: 48px; line-height: 48px;
color: @blHighlightColor; color: @blHighlightColor;
background: #6d3df514; background: #6D3DF514;
border-radius: 8px; border-radius: 8px;
padding: 32px 72px; padding: 32px 72px;
margin-top: 40px; margin-top: 40px;
@ -72,7 +73,7 @@
&__describe { &__describe {
.describe-font(); .describe-font();
margin-top: 24px; margin-top: 24px
} }
&__content { &__content {
@ -132,7 +133,6 @@
&__header { &__header {
.flex-row(); .flex-row();
.header-font(); .header-font();
font-size: 32px;
.highlight { .highlight {
color: @blHighlightColor; color: @blHighlightColor;
@ -143,8 +143,7 @@
&__describe { &__describe {
.flex-row(); .flex-row();
.describe-font(); .describe-font();
margin-top: 18px; margin-top: 24px;
line-height: 40px;
.highlight { .highlight {
color: @blHighlightColor; color: @blHighlightColor;
@ -159,21 +158,21 @@
&__item { &__item {
position: relative; position: relative;
width: 182px; width: 170px;
height: 192px; height: 192px;
.flex-column(); .flex-column();
justify-content: center; justify-content: center;
border: 2px solid @blHighlightColor; border: 2px solid @blHighlightColor;
border-radius: 8px; border-radius: 8px;
margin-right: 15px; margin-right: 24px;
&:last-child { &:last-child {
margin-right: 0; margin-right: 0;
} }
&.disabled { &.disabled {
border-color: #e0e0e0; border-color: #E0E0E0;
background: #f7f7f7; background: #F7F7F7;
} }
&.selected { &.selected {
@ -225,18 +224,6 @@
margin-top: 32px; margin-top: 32px;
} }
&__hint {
font-size: 24px;
line-height: 40px;
text-align: center;
color: @blColorG1;
margin-top: 24px;
.highlight {
color: @blHighlightColor;
display: inline;
}
}
} }
// ============================================= 群 ================================================= // // ============================================= 群 ================================================= //
@ -320,7 +307,7 @@
line-height: 36px; line-height: 36px;
text-align: center; text-align: center;
border-radius: 50%; border-radius: 50%;
border: 2px solid #e0e0e0; border: 2px solid #E0E0E0;
} }
&__qr-code__step-title { &__qr-code__step-title {
@ -335,7 +322,7 @@
position: relative; position: relative;
width: 2px; width: 2px;
height: 40px; height: 40px;
background: #e0e0e0; background: #E0E0E0;
margin: 4px 0; margin: 4px 0;
margin-left: 18px; margin-left: 18px;
} }
@ -362,7 +349,7 @@
.divider { .divider {
width: 540px; width: 540px;
height: 1px; height: 1px;
background: #e0e0e0; background: #E0E0E0;
margin: 40px 0; margin: 40px 0;
} }
@ -414,7 +401,7 @@
margin-top: 40px; margin-top: 40px;
.highlight { .highlight {
color: @blHighlightColor; color: @blHighlightColor
} }
} }
@ -520,6 +507,7 @@
.button(); .button();
} }
// ============================================= 发布通告的企业会员 ================================================= // // ============================================= 发布通告的企业会员 ================================================= //
&__publish-job-buy__header { &__publish-job-buy__header {
.header-font(); .header-font();
@ -572,21 +560,21 @@
&__item { &__item {
position: relative; position: relative;
width: 182px; width: 230px;
height: 156px; height: 156px;
.flex-column(); .flex-column();
justify-content: center; justify-content: center;
border: 2px solid @blHighlightColor; border: 2px solid @blHighlightColor;
border-radius: 8px; border-radius: 8px;
margin-right: 15px; margin-right: 24px;
&:last-child { &:last-child {
margin-right: 0; margin-right: 0;
} }
&.disabled { &.disabled {
border-color: #e0e0e0; border-color: #E0E0E0;
background: #f7f7f7; background: #F7F7F7;
} }
&.selected { &.selected {
@ -631,11 +619,11 @@
} }
&__left-line { &__left-line {
background: linear-gradient(270deg, #e0e0e0 0%, #ffffff 100%); background: linear-gradient(270deg, #E0E0E0 0%, #FFFFFF 100%);
} }
&__right-line { &__right-line {
background: linear-gradient(90deg, #e0e0e0 0%, #ffffff 100%); background: linear-gradient(90deg, #E0E0E0 0%, #FFFFFF 100%);
} }
&__title { &__title {
@ -664,11 +652,6 @@
&.highlight { &.highlight {
color: @blHighlightColor; color: @blHighlightColor;
} }
.highlight-span {
color: @blHighlightColor;
display: inline;
}
} }
&__button { &__button {
@ -676,4 +659,5 @@
margin-top: 32px; margin-top: 32px;
} }
} }
} }

View File

@ -1,174 +0,0 @@
import Taro from '@tarojs/taro';
import { Dialog } from '@taroify/core';
import { useCallback, useEffect, useState } from 'react';
import { DialogStatus, PREFIX } from '@/components/product-dialog/const';
import JobBuy from '@/components/product-dialog/steps-ui/job-buy';
import ContactCustomerService from '@/components/product-dialog/steps-ui/job-contact-customer';
import ContactDirect from '@/components/product-dialog/steps-ui/job-contact-direct';
import UnableUnlockContent from '@/components/product-dialog/steps-ui/job-unable';
import { DeclarationType, ProductType } from '@/constants/product';
import { JobDetails } from '@/types/job';
import { GetProductIsUnlockResponse, ProductInfo } from '@/types/product';
import { logWithPrefix } from '@/utils/common';
import { requestUseProduct } from '@/utils/product';
import Toast from '@/utils/toast';
import '../index.less';
interface IProps {
data: JobDetails;
/** Product use record from parent - if exists, user has already unlocked this job */
productRecord?: GetProductIsUnlockResponse | null;
/** Product balance info from parent */
productInfo?: ProductInfo;
/** Callback to refresh product balance in parent after purchase */
onRefreshBalance?: () => Promise<ProductInfo | undefined>;
onClose: () => void;
}
const PRODUCT_CODE = ProductType.VIP;
const log = logWithPrefix('product-job-contact-dialog');
/**
* Integrated dialog component for job contact flow
* Handles: balance check -> buy if needed -> use product -> show contact info
*
* @param productRecord - Pass from parent to avoid duplicate API calls
* @param productInfo - Pass from parent to avoid duplicate API calls
* @param onRefreshBalance - Callback to refresh balance in parent after purchase
*/
function ProductJobContactDialog(props: IProps) {
const { data, productRecord, productInfo, onRefreshBalance, onClose } = props;
const [status, setStatus] = useState<DialogStatus>(DialogStatus.LOADING);
const [publisherAcctNo, setPublisherAcctNo] = useState<string>('');
/**
* Handle contact display based on declaration type
*/
const showContactResult = useCallback((declarationTypeResult?: ProductInfo['declarationTypeResult']) => {
if (declarationTypeResult?.type === DeclarationType.Direct && declarationTypeResult.publisherAcctNo) {
log('show JOB_CONTACT_DIRECT', declarationTypeResult.publisherAcctNo);
setPublisherAcctNo(declarationTypeResult.publisherAcctNo);
setStatus(DialogStatus.JOB_CONTACT_DIRECT);
} else {
log('show JOB_CONTACT_CS');
setStatus(DialogStatus.JOB_CONTACT_CS);
}
}, []);
/**
* Use product and show contact info
*/
const consumeProductAndShowContact = useCallback(async () => {
const productResult = await requestUseProduct(PRODUCT_CODE, { jobId: data.id });
log('consumeProductAndShowContact result', productResult);
// Refresh balance in parent after consuming product
await onRefreshBalance?.();
showContactResult(productResult?.declarationTypeResult);
}, [data.id, showContactResult, onRefreshBalance]);
/**
* Callback after successful purchase
* Refresh balance via parent callback and use product to show contact info
*/
const handleAfterBuy = useCallback(async () => {
log('handleAfterBuy - start');
try {
Taro.showLoading({ mask: true, title: '加载中...' });
// Refresh balance via parent callback
await onRefreshBalance?.();
// Use product and show contact info after purchase
await consumeProductAndShowContact();
} catch (e) {
log('handleAfterBuy error', e);
Toast.error('出错了,请重试');
onClose();
} finally {
Taro.hideLoading();
}
}, [consumeProductAndShowContact, onRefreshBalance, onClose]);
const handleReport = useCallback(() => {
log('report', data.id);
}, [data.id]);
/**
* Initialize dialog on mount
*/
useEffect(() => {
let isMounted = true;
const init = async () => {
try {
Taro.showLoading({ mask: true, title: '加载中...' });
log('init with productRecord', productRecord);
log('init with productInfo', productInfo);
// Step 1: Already unlocked - show contact directly
if (productRecord) {
log('show JOB_CONTACT_DIRECT from productRecord', productRecord.declarationTypeResult);
showContactResult(productRecord.declarationTypeResult);
return;
}
// Step 2: No productInfo - error state
if (!productInfo) {
log('no productInfo provided, closing');
Toast.error('出错了,请重试');
onClose();
return;
}
// Step 3: Determine status based on balance
if (!productInfo.isPaidVip && !productInfo.freeBalance) {
log('show JOB_BUY');
if (isMounted) setStatus(DialogStatus.JOB_BUY);
} else if (!productInfo.balance) {
log('show JOB_UNABLE_UNLOCK');
if (isMounted) setStatus(DialogStatus.JOB_UNABLE_UNLOCK);
} else {
await consumeProductAndShowContact();
}
} catch (e) {
log('init error', e);
Toast.error('出错了,请重试');
onClose();
} finally {
Taro.hideLoading();
}
};
init();
return () => {
isMounted = false;
};
// Only run on mount - props are captured at dialog open time
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (status === DialogStatus.LOADING) {
return null;
}
return (
<Dialog className={PREFIX} onClose={onClose} open>
<Dialog.Content>
{status === DialogStatus.JOB_CONTACT_CS && <ContactCustomerService onAfterConfirm={onClose} />}
{status === DialogStatus.JOB_CONTACT_DIRECT && (
<ContactDirect publisherAcctNo={publisherAcctNo} onAfterConfirm={onClose} onReport={handleReport} />
)}
{status === DialogStatus.JOB_BUY && (
<JobBuy onConfirm={handleAfterBuy} isCreateResume={productInfo?.isCreateResume} />
)}
{status === DialogStatus.JOB_UNABLE_UNLOCK && <UnableUnlockContent onConfirm={onClose} />}
</Dialog.Content>
</Dialog>
);
}
export default ProductJobContactDialog;

View File

@ -102,7 +102,7 @@ function ProductJobWithGroupDialog(props: Omit<IProps, 'visible'>) {
return; return;
} }
// 否则:如果有解锁次数,显示是否确定消费。无解锁次数,显示不无次数 UI // 否则:如果有解锁次数,显示是否确定消费。无解锁次数,显示不无次数 UI
const [time] = await requestProductBalance(ProductType.AddGroup); const time = await requestProductBalance(ProductType.AddGroup);
if (time <= 0) { if (time <= 0) {
setStatus(DialogStatus.JOB_CONTACT_NEED_BUY_GROUP); setStatus(DialogStatus.JOB_CONTACT_NEED_BUY_GROUP);
} else { } else {
@ -119,7 +119,7 @@ function ProductJobWithGroupDialog(props: Omit<IProps, 'visible'>) {
return; return;
} }
// 自动报单 // 自动报单
const [time] = await requestProductBalance(ProductType.GetJob); const time = await requestProductBalance(ProductType.GetJob);
if (time <= 0) { if (time <= 0) {
setStatus(DialogStatus.JOB_UNABLE_UNLOCK); setStatus(DialogStatus.JOB_UNABLE_UNLOCK);
} else { } else {

View File

@ -41,7 +41,7 @@ function ProductJobDialog(props: Omit<IProps, 'visible'>) {
}, [onClose]); }, [onClose]);
const handleAfterBuy = useCallback(async () => { const handleAfterBuy = useCallback(async () => {
const [time] = await requestProductBalance(PRODUCT_CODE); const time = await requestProductBalance(PRODUCT_CODE);
if (time <= 0) { if (time <= 0) {
Toast.error('发生错误请重试'); Toast.error('发生错误请重试');
onClose(); onClose();
@ -83,7 +83,7 @@ function ProductJobDialog(props: Omit<IProps, 'visible'>) {
handleContact(result.declarationTypeResult); handleContact(result.declarationTypeResult);
return; return;
} }
const [time] = await requestProductBalance(PRODUCT_CODE); const time = await requestProductBalance(PRODUCT_CODE);
if (time <= 0) { if (time <= 0) {
const allowBuy = await requestAllBuyProduct(PRODUCT_CODE); const allowBuy = await requestAllBuyProduct(PRODUCT_CODE);
setStatus(allowBuy ? DialogStatus.JOB_BUY : DialogStatus.JOB_UNABLE_UNLOCK); setStatus(allowBuy ? DialogStatus.JOB_BUY : DialogStatus.JOB_UNABLE_UNLOCK);

View File

@ -99,8 +99,12 @@ export function CompanyPublishJobDialog(props: IProps) {
const handleNext = useCallback(async () => { const handleNext = useCallback(async () => {
onClose(true); onClose(true);
if (userInfo.bossAuthStatus) {
navigateTo(PageUrl.JobPublish); navigateTo(PageUrl.JobPublish);
}, [onClose]); } else {
navigateTo(PageUrl.CertificationStart);
}
}, [userInfo, onClose]);
useEffect(() => { useEffect(() => {
initRef.current = async () => { initRef.current = async () => {
@ -111,7 +115,7 @@ export function CompanyPublishJobDialog(props: IProps) {
try { try {
const productCode = ProductType.CompanyPublishJob; const productCode = ProductType.CompanyPublishJob;
Taro.showLoading(); Taro.showLoading();
const [time] = await requestProductBalance(productCode); const time = await requestProductBalance(productCode);
if (time <= 0) { if (time <= 0) {
setStatus(DialogStatus.COMPANY_PUBLISH_JOB_BUY); setStatus(DialogStatus.COMPANY_PUBLISH_JOB_BUY);
return; return;

View File

@ -2,23 +2,16 @@ import Taro from '@tarojs/taro';
import { Button } from '@taroify/core'; import { Button } from '@taroify/core';
import classNames from 'classnames'; import classNames from 'classnames';
import { Fragment, useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import Badge from '@/components/badge'; import Badge from '@/components/badge';
import { PREFIX } from '@/components/product-dialog/const'; import { PREFIX } from '@/components/product-dialog/const';
import { CollectEventName, ReportEventId } from '@/constants/event'; import { CollectEventName, ReportEventId } from '@/constants/event';
import { OrderStatus, OrderType, ProductSpecId, ProductType } from '@/constants/product'; import { OrderStatus, OrderType, ProductSpecId, ProductType } from '@/constants/product';
import { SubscribeTempId } from '@/constants/subscribe'; import { SubscribeTempId } from '@/constants/subscribe';
import { ProductSpecResult } from '@/types/product';
import { logWithPrefix } from '@/utils/common'; import { logWithPrefix } from '@/utils/common';
import { collectEvent, reportEvent } from '@/utils/event'; import { collectEvent, reportEvent } from '@/utils/event';
import { import { getOrderPrice, isCancelPay, requestCreatePayInfo, requestOrderInfo, requestPayment } from '@/utils/product';
isCancelPay,
requestCreatePayInfo,
requestOrderInfo,
requestPayment,
requestProductTypeList,
} from '@/utils/product';
import { postSubscribe, subscribeMessage } from '@/utils/subscribe'; import { postSubscribe, subscribeMessage } from '@/utils/subscribe';
import Toast from '@/utils/toast'; import Toast from '@/utils/toast';
@ -33,51 +26,53 @@ interface Item {
title: string; title: string;
price: string; price: string;
amt: number; amt: number;
contents: { content: string; highlight?: boolean; inlineHighlight?: string }[]; contents: { content: string; highlight?: boolean }[];
badge?: string; badge?: string;
} }
const LIST: Item[] = [ const LIST: Item[] = [
// {
// id: ProductSpecId.BOSS_VIP_NEW_1,
// title: '展示一周',
// price: '480播豆',
// amt: 48,
// badge: '限时体验',
// contents: [
// { content: '-通告每日优先展示' },
// { content: '-每天可查看20个主播详情' },
// { content: '-每天可主动联系10个主播' },
// // { content: '-播络可代为联系20个主播高成功率', highlight: true },
// { content: '-有效期一周' },
// ],
// },
{ {
id: ProductSpecId.BOSS_VIP_NEW_2, id: ProductSpecId.BOSS_VIP_NEW_1,
title: '推荐一月', title: '展示一周',
price: '680播豆', price: '480播豆',
amt: 68, amt: 48,
badge: '限时体验', badge: '限时体验',
contents: [ contents: [
{ content: '-通告每日优先展示', highlight: true }, { content: '-1个通告' },
{ content: '-每天可查看20个主播详情', inlineHighlight: '可主动联系10个' }, { content: '-每天可查看20个主播详情' },
{ content: '-每天可自主联系10个主播' },
// { content: '-播络可代为联系20个主播高成功率', highlight: true },
{ content: '-有效期一周' },
],
},
{
id: ProductSpecId.BOSS_VIP_NEW_2,
title: '展示一月',
price: '960播豆',
amt: 96,
badge: '五折',
contents: [
{ content: '-1个通告' },
{ content: '-每天可查看20个主播详情' },
{ content: '-每天可自主联系10个主播' },
// { content: '-播络可代为联系20个主播高成功率', highlight: true }, // { content: '-播络可代为联系20个主播高成功率', highlight: true },
{ content: '-有效期一个月' }, { content: '-有效期一个月' },
], ],
}, },
{ // {
id: ProductSpecId.BOSS_VIP_NEW_3, // id: ProductSpecId.BOSS_VIP_NEW_3,
title: '推荐一季', // title: '季会员',
price: '1360播豆', // price: '2680播豆',
amt: 136, // amt: 268,
badge: '6.7折', // badge: '7折',
contents: [ // contents: [
{ content: '-通告每日优先展示', highlight: true }, // { content: '-1个通告' },
{ content: '-每天可查看20个主播详情', inlineHighlight: '可主动联系10个' }, // { content: '-每天可查看20个主播详情' },
// { content: '-每天可自主联系10个主播' },
// { content: '-播络可代为联系60个主播高成功率', highlight: true }, // { content: '-播络可代为联系60个主播高成功率', highlight: true },
{ content: '-有效期一个季度' }, // { content: '-有效期3个月' },
], // ],
}, // },
]; ];
const subscribe = async () => { const subscribe = async () => {
@ -89,35 +84,22 @@ const subscribe = async () => {
postSubscribe(TEMP_IDS, successIds); postSubscribe(TEMP_IDS, successIds);
}; };
const getJsonContent = (jsonString: string) => {
try {
return JSON.parse(jsonString);
} catch (e) {
return [];
}
};
export default function CompanyPublishJobBuy(props: IProps) { export default function CompanyPublishJobBuy(props: IProps) {
const { onNext } = props; const { onNext } = props;
const [productList, setProductList] = useState<ProductSpecResult[]>([]); const [selectItem, setSelectItem] = useState(LIST[0]);
const [selectItem, setSelectItem] = useState<ProductSpecResult | undefined>();
const handleClickItem = useCallback((newSelectItem: ProductSpecResult) => setSelectItem(newSelectItem), []); const handleClickItem = useCallback((newSelectItem: Item) => setSelectItem(newSelectItem), []);
const handleBuy = useCallback(async () => { const handleBuy = useCallback(async () => {
log('handleBuy'); log('handleBuy');
reportEvent(ReportEventId.CLICK_PAY_PUBLISH_JOB); reportEvent(ReportEventId.CLICK_PAY_PUBLISH_JOB);
if (!selectItem) {
Toast.error('请选择购买的产品');
return;
}
try { try {
Taro.showLoading(); Taro.showLoading();
const { payOrderNo, createPayInfo } = await requestCreatePayInfo({ const { payOrderNo, createPayInfo } = await requestCreatePayInfo({
type: OrderType.CompanyVip, type: OrderType.CompanyVip,
amt: selectItem.payPrice, amt: getOrderPrice(selectItem.amt),
productCode: ProductType.BOSS_VIP_NEW, productCode: ProductType.BOSS_VIP_NEW,
productSpecId: selectItem.productSpecId, productSpecId: selectItem.id,
}); });
log('handleBuy payInfo', payOrderNo, createPayInfo); log('handleBuy payInfo', payOrderNo, createPayInfo);
await requestPayment({ await requestPayment({
@ -141,14 +123,8 @@ export default function CompanyPublishJobBuy(props: IProps) {
log('handleBuy error', e); log('handleBuy error', e);
} }
}, [selectItem, onNext]); }, [selectItem, onNext]);
const handleGetProductInfo = useCallback(async () => {
const result = await requestProductTypeList(ProductType.BOSS_VIP_NEW);
setProductList(result);
setSelectItem(result[0]);
}, []);
useEffect(() => { useEffect(() => {
handleGetProductInfo();
collectEvent(CollectEventName.CREATE_ORDER_VIEW, { orderType: OrderType.BossVip, source: 'user-page' }); collectEvent(CollectEventName.CREATE_ORDER_VIEW, { orderType: OrderType.BossVip, source: 'user-page' });
}, []); }, []);
@ -156,21 +132,19 @@ export default function CompanyPublishJobBuy(props: IProps) {
<div className={`${PREFIX}__company-publish-job-buy`}> <div className={`${PREFIX}__company-publish-job-buy`}>
<div className={`${PREFIX}__company-publish-job-buy__header`}></div> <div className={`${PREFIX}__company-publish-job-buy__header`}></div>
<div className={`${PREFIX}__company-publish-job-buy__price-container`}> <div className={`${PREFIX}__company-publish-job-buy__price-container`}>
{productList.map(item => ( {LIST.map(item => (
<div <div
key={item.payPrice} key={item.price}
className={classNames(`${PREFIX}__company-publish-job-buy__item`, { className={classNames(`${PREFIX}__company-publish-job-buy__item`, {
selected: selectItem && item.id === selectItem.id, selected: item.amt === selectItem.amt,
disabled: item.payPrice === 0, disabled: item.amt === 0,
})} })}
onClick={item.payPrice === 0 ? undefined : () => handleClickItem(item)} onClick={item.amt === 0 ? undefined : () => handleClickItem(item)}
>
<div
className={classNames(`${PREFIX}__company-publish-job-buy__item__title`, { free: item.payPrice === 0 })}
> >
<div className={classNames(`${PREFIX}__company-publish-job-buy__item__title`, { free: item.amt === 0 })}>
{item.title} {item.title}
</div> </div>
<div className={`${PREFIX}__company-publish-job-buy__item__price`}>{item.priceText}</div> <div className={`${PREFIX}__company-publish-job-buy__item__price`}>{item.price}</div>
{item.badge && <Badge className={`${PREFIX}__company-publish-job-buy__item__badge`} text={item.badge} />} {item.badge && <Badge className={`${PREFIX}__company-publish-job-buy__item__badge`} text={item.badge} />}
</div> </div>
))} ))}
@ -180,24 +154,19 @@ export default function CompanyPublishJobBuy(props: IProps) {
<div className={`${PREFIX}__company-publish-job-buy__divider__title`}></div> <div className={`${PREFIX}__company-publish-job-buy__divider__title`}></div>
<div className={`${PREFIX}__company-publish-job-buy__divider__right-line`} /> <div className={`${PREFIX}__company-publish-job-buy__divider__right-line`} />
</div> </div>
{selectItem && (
<Fragment>
<div className={`${PREFIX}__company-publish-job-buy__contents`}> <div className={`${PREFIX}__company-publish-job-buy__contents`}>
{getJsonContent(selectItem.contentsJson).map(i => ( {selectItem.contents.map(i => (
<div <div
className={classNames(`${PREFIX}__company-publish-job-buy__content`, { highlight: i.highlight })} className={classNames(`${PREFIX}__company-publish-job-buy__content`, { highlight: i.highlight })}
key={i.content} key={i.content}
> >
{i.content} {i.content}
{i.inlineHighlight && <div className="highlight-span">{i.inlineHighlight}</div>}
</div> </div>
))} ))}
</div> </div>
<Button className={`${PREFIX}__company-publish-job-buy__button`} onClick={handleBuy}> <Button className={`${PREFIX}__company-publish-job-buy__button`} onClick={handleBuy}>
{`支付${selectItem.showPrice}`} {`支付${selectItem.amt}`}
</Button> </Button>
</Fragment>
)}
</div> </div>
); );
} }

View File

@ -70,7 +70,7 @@ export default function GroupBuy(props: IProps) {
if (status !== OrderStatus.Success) { if (status !== OrderStatus.Success) {
throw new Error('order status error'); throw new Error('order status error');
} }
const [time] = await requestProductBalance(ProductType.AddGroup); const time = await requestProductBalance(ProductType.AddGroup);
log('handleBuy new addGroupTime', time); log('handleBuy new addGroupTime', time);
onConfirm(time); onConfirm(time);
} catch (e) { } catch (e) {

View File

@ -2,64 +2,51 @@ import Taro from '@tarojs/taro';
import { Button } from '@taroify/core'; import { Button } from '@taroify/core';
import classNames from 'classnames'; import classNames from 'classnames';
import { Fragment, useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import Badge from '@/components/badge'; import Badge from '@/components/badge';
import { PREFIX } from '@/components/product-dialog/const'; import { PREFIX } from '@/components/product-dialog/const';
import { PageUrl } from '@/constants/app';
import { CollectEventName } from '@/constants/event'; import { CollectEventName } from '@/constants/event';
import { OrderStatus, OrderType, ProductType } from '@/constants/product'; import { OrderStatus, OrderType, ProductSpecId, ProductType } from '@/constants/product';
import { SubscribeTempId } from '@/constants/subscribe'; import { SubscribeTempId } from '@/constants/subscribe';
import { ProductSpecResult } from '@/types/product';
import { logWithPrefix } from '@/utils/common'; import { logWithPrefix } from '@/utils/common';
import { collectEvent } from '@/utils/event'; import { collectEvent } from '@/utils/event';
import { import { getOrderPrice, isCancelPay, requestCreatePayInfo, requestOrderInfo, requestPayment } from '@/utils/product';
isCancelPay,
requestCreatePayInfo,
requestOrderInfo,
requestPayment,
requestProductTypeList,
} from '@/utils/product';
import { navigateTo } from '@/utils/route';
import { postSubscribe, subscribeMessage } from '@/utils/subscribe'; import { postSubscribe, subscribeMessage } from '@/utils/subscribe';
import Toast from '@/utils/toast'; import Toast from '@/utils/toast';
interface IProps { interface IProps {
onConfirm: () => void; onConfirm: () => void;
isCreateResume?: boolean;
} }
// interface Item { interface Item {
// id: ProductSpecId; id: ProductSpecId;
// title: string; title: string;
// content: string; content: string;
// buyOnlyContent?: string; price: string;
// price: string; amt: number;
// amt: number; badge?: string;
// badge?: string; }
// }
// const LIST: Item[] = [ const LIST: Item[] = [
// { id: ProductSpecId.WeeklyVIP, title: '非会员', content: '每日2次', price: '免费', amt: 0 }, { id: ProductSpecId.WeeklyVIP, title: '非会员', content: '每日2次', price: '免费', amt: 0 },
// { {
// id: ProductSpecId.DailyVIP, id: ProductSpecId.WeeklyVIP,
// title: '日会员', title: '周会员',
// content: '每日+10次', content: '每日5次',
// buyOnlyContent: '每日12次', price: '60播豆',
// price: '60播豆', amt: 6,
// amt: 6, badge: '限时体验',
// badge: '限时体验', },
// }, {
// { id: ProductSpecId.NewMonthlyVIP,
// id: ProductSpecId.WeeklyVIP, title: '月会员',
// title: '周会员', content: '每日5次',
// content: '每日+10次', price: '180播豆',
// buyOnlyContent: '每日12次', amt: 18,
// price: '180播豆', badge: ' 超值',
// amt: 18, },
// badge: ' 超值', ];
// },
// ];
const log = logWithPrefix('job-buy'); const log = logWithPrefix('job-buy');
const SUBSCRIBE_ID = SubscribeTempId.SUBSCRIBE_VIP; const SUBSCRIBE_ID = SubscribeTempId.SUBSCRIBE_VIP;
@ -75,26 +62,20 @@ const subscribe = async () => {
}; };
export default function JobBuy(props: IProps) { export default function JobBuy(props: IProps) {
const { onConfirm, isCreateResume } = props; const { onConfirm } = props;
const [productList, setProductList] = useState<ProductSpecResult[]>([]); const [selectItem, setSelectItem] = useState(LIST[1]);
const [selectItem, setSelectItem] = useState<ProductSpecResult | undefined>();
const handleClickItem = useCallback((newSelectItem: ProductSpecResult) => setSelectItem(newSelectItem), []); const handleClickItem = useCallback((newSelectItem: Item) => setSelectItem(newSelectItem), []);
const handleBuy = useCallback(async () => { const handleBuy = useCallback(async () => {
log('handleBuy', selectItem); log('handleBuy', selectItem);
if (!selectItem) {
Toast.error('请选择购买的产品');
return;
}
try { try {
Taro.showLoading(); Taro.showLoading();
const { payOrderNo, createPayInfo } = await requestCreatePayInfo({ const { payOrderNo, createPayInfo } = await requestCreatePayInfo({
type: OrderType.VIP, type: OrderType.VIP,
amt: selectItem.payPrice, amt: getOrderPrice(selectItem.amt),
productCode: ProductType.VIP, productCode: ProductType.VIP,
productSpecId: selectItem.productSpecId, productSpecId: selectItem.id,
}); });
log('handleBuy payInfo', payOrderNo, createPayInfo); log('handleBuy payInfo', payOrderNo, createPayInfo);
await requestPayment({ await requestPayment({
@ -119,89 +100,41 @@ export default function JobBuy(props: IProps) {
} }
}, [selectItem, onConfirm]); }, [selectItem, onConfirm]);
const handleGetProductInfo = useCallback(async () => {
const result = await requestProductTypeList(ProductType.VIP);
setProductList(result);
setSelectItem(result[0]);
}, []);
const handleResume = useCallback(() => {
navigateTo(PageUrl.MaterialUploadVideo);
}, []);
useEffect(() => { useEffect(() => {
handleGetProductInfo();
collectEvent(CollectEventName.CREATE_ORDER_VIEW, { orderType: OrderType.VIP }); collectEvent(CollectEventName.CREATE_ORDER_VIEW, { orderType: OrderType.VIP });
}, []); }, []);
return ( return (
<div className={`${PREFIX}__job-buy`}> <div className={`${PREFIX}__job-buy`}>
{isCreateResume ? (
<Fragment>
<div className={`${PREFIX}__job-buy__header`}> <div className={`${PREFIX}__job-buy__header`}>
<div></div> <div></div>
<div className="highlight"></div> <div className="highlight"></div>
</div> </div>
<div className={`${PREFIX}__job-buy__describe`}> <div className={`${PREFIX}__job-buy__describe`}>
<div></div> <div></div>
<div className="highlight"></div> <div className="highlight"></div>
<div></div> <div> </div>
<div> </div>
<div className="highlight"></div> <div className="highlight"></div>
</div> </div>
</Fragment>
) : (
<Fragment>
<div className={`${PREFIX}__job-buy__header`}>
<div></div>
<div className="highlight"></div>
</div>
<div className={`${PREFIX}__job-buy__describe`}>
<div></div>
<div className="highlight"></div>
<div></div>
<div> </div>
<div className="highlight"></div>
</div>
<div className={`${PREFIX}__job-buy__describe`}>
<div> </div>
<div className="highlight" onClick={handleResume}>
</div>
</div>
</Fragment>
)}
<div className={`${PREFIX}__job-buy__container`}> <div className={`${PREFIX}__job-buy__container`}>
{productList.map(item => { {LIST.map(item => (
return (
<div <div
key={item.id} key={item.price}
className={classNames(`${PREFIX}__job-buy__item`, { className={classNames(`${PREFIX}__job-buy__item`, {
selected: selectItem && item.id === selectItem.id, selected: item.amt === selectItem.amt,
disabled: item.payPrice === 0, disabled: item.amt === 0,
})} })}
onClick={item.payPrice === 0 ? undefined : () => handleClickItem(item)} onClick={item.amt === 0 ? undefined : () => handleClickItem(item)}
> >
<div className={classNames(`${PREFIX}__job-buy__item__title`, { free: item.payPrice === 0 })}> <div className={classNames(`${PREFIX}__job-buy__item__title`, { free: item.amt === 0 })}>{item.title}</div>
{item.title} <div className={`${PREFIX}__job-buy__item__content`}>{item.content}</div>
</div> <div className={`${PREFIX}__job-buy__item__price`}>{item.price}</div>
<div className={`${PREFIX}__job-buy__item__content`}>{item.contentSingle}</div>
<div className={`${PREFIX}__job-buy__item__price`}>{item.showPrice}</div>
{item.badge && <Badge className={`${PREFIX}__job-buy__item__badge`} text={item.badge} />} {item.badge && <Badge className={`${PREFIX}__job-buy__item__badge`} text={item.badge} />}
</div> </div>
); ))}
})}
</div> </div>
{selectItem && ( <Button className={`${PREFIX}__job-buy__button`} onClick={handleBuy}>
<div className={`${PREFIX}__job-buy__hint`}> {`支付 ${selectItem.amt}`}
{selectItem?.title}
<div className="highlight">
{selectItem?.expire > 1 ? `${selectItem.expire}` : `${selectItem.expire * 24}小时`}
</div>
</div>
)}
<Button className={`${PREFIX}__job-buy__button`} disabled={!selectItem} onClick={handleBuy}>
{`支付 ${selectItem?.showPrice}`}
</Button> </Button>
{/* <div className={`${PREFIX}__job-buy__tips`}>{`已选:${selectItem.title},含进本地群服务`}</div> */} {/* <div className={`${PREFIX}__job-buy__tips`}>{`已选:${selectItem.title},含进本地群服务`}</div> */}
</div> </div>

View File

@ -10,7 +10,7 @@ interface IContactDirectProps {
} }
export default function ContactDirect(props: IContactDirectProps) { export default function ContactDirect(props: IContactDirectProps) {
const { publisherAcctNo, onAfterConfirm } = props; const { publisherAcctNo, onAfterConfirm, onReport } = props;
const handleCopyAndContact = async () => { const handleCopyAndContact = async () => {
await copy(publisherAcctNo); await copy(publisherAcctNo);

View File

@ -2,10 +2,9 @@ import { BaseEventOrig, InputProps, ScrollView } from '@tarojs/components';
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import { Search } from '@taroify/core'; import { Search } from '@taroify/core';
import { Fragment, useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { CITY_CODE_TO_NAME_MAP, CITY_INDEXES_LIST } from '@/constants/city'; import { CITY_CODE_TO_NAME_MAP, CITY_INDEXES_LIST, GROUP_CITY_INDEXES_LIST } from '@/constants/city';
import useCityOperators from '@/hooks/use-city-operators';
import { logWithPrefix } from '@/utils/common'; import { logWithPrefix } from '@/utils/common';
import './index.less'; import './index.less';
@ -69,19 +68,8 @@ export default function SearchCity({
const [touchAnchor, setTouchAnchor] = useState<string | undefined>(); const [touchAnchor, setTouchAnchor] = useState<string | undefined>();
const [touchMoving, setTouchMoving] = useState(false); const [touchMoving, setTouchMoving] = useState(false);
const [searchResult, setSearchResult] = useState<Item[]>([]); const [searchResult, setSearchResult] = useState<Item[]>([]);
const cityOperators = useCityOperators();
const groupCityCodes = cityOperators.map(it => (it.groupLink ? it.cityCode : null)).filter(Boolean);
const showSearchList = searchResult.length > 0; const showSearchList = searchResult.length > 0;
const CITY_LIST = forGroup const CITY_LIST = forGroup ? GROUP_CITY_INDEXES_LIST : CITY_INDEXES_LIST;
? CITY_INDEXES_LIST.map(({ letter, data }) => {
return {
letter,
data: data.filter(it => groupCityCodes.includes(it.cityCode)),
};
}).filter(item => item.data.length > 0)
: CITY_INDEXES_LIST;
const HOT_CITY_LIST = forGroup ? HOT_CITY.filter(it => groupCityCodes.includes(`${it.cityCode}`)) : HOT_CITY;
const handleSearchChange = useCallback((event: BaseEventOrig<InputProps.inputEventDetail>) => { const handleSearchChange = useCallback((event: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = event.detail.value; const value = event.detail.value;
@ -185,12 +173,9 @@ export default function SearchCity({
<div> <div>
<div className={`${PREFIX}__position-title`}></div> <div className={`${PREFIX}__position-title`}></div>
<div className={`${PREFIX}__position-city`}>{CITY_CODE_TO_NAME_MAP.get(currentCity)}</div> <div className={`${PREFIX}__position-city`}>{CITY_CODE_TO_NAME_MAP.get(currentCity)}</div>
{!!HOT_CITY_LIST.length && (
<Fragment>
<div className={`${PREFIX}__hot-city-title`}></div> <div className={`${PREFIX}__hot-city-title`}></div>
<div className={`${PREFIX}__hot-city-container`}> <div className={`${PREFIX}__hot-city-container`}>
{HOT_CITY_LIST.map(city => { {HOT_CITY.map(city => (
return (
<div <div
key={city.cityCode} key={city.cityCode}
className={`${PREFIX}__hot-city-item`} className={`${PREFIX}__hot-city-item`}
@ -199,11 +184,8 @@ export default function SearchCity({
> >
{city.cityName} {city.cityName}
</div> </div>
); ))}
})}
</div> </div>
</Fragment>
)}
<div className={`${PREFIX}__indexes-list`}> <div className={`${PREFIX}__indexes-list`}>
{CITY_LIST.map(item => { {CITY_LIST.map(item => {
return ( return (

View File

@ -28,7 +28,6 @@
justify-content: space-between; justify-content: space-between;
box-sizing: border-box; box-sizing: border-box;
padding: 0 24px; padding: 0 24px;
flex: 0 0 96px;
&.selected { &.selected {
color: @blHighlightColor; color: @blHighlightColor;

View File

@ -1,11 +1,14 @@
import { Tabbar } from '@taroify/core'; import { Tabbar } from '@taroify/core';
import classNames from 'classnames'; import classNames from 'classnames';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { APP_TAB_BAR_ID, RoleType, PageType, PageUrl } from '@/constants/app'; import { APP_TAB_BAR_ID, RoleType, PageType, PageUrl } from '@/constants/app';
import { ANCHOR_TAB_LIST, COMPANY_TAB_LIST, TabItemType } from '@/hooks/use-config'; import { ANCHOR_TAB_LIST, COMPANY_TAB_LIST, TabItemType } from '@/hooks/use-config';
import useMessage from '@/hooks/use-message'; import useMessage from '@/hooks/use-message';
import useRoleType from '@/hooks/user-role-type'; import useRoleType from '@/hooks/user-role-type';
import { changeHomePage } from '@/store/actions';
import { selectHomePageType } from '@/store/selector';
import { logWithPrefix } from '@/utils/common'; import { logWithPrefix } from '@/utils/common';
import { switchTab } from '@/utils/route'; import { switchTab } from '@/utils/route';
@ -13,7 +16,6 @@ import './index.less';
interface IProps { interface IProps {
className?: string; className?: string;
type: PageType;
} }
const PREFIX = 'base-tab-bar'; const PREFIX = 'base-tab-bar';
@ -38,21 +40,25 @@ const TabItem = (props: { item: TabItemType }) => {
); );
}; };
function BaseTabBar({ className, type }: IProps) { function BaseTabBar(props: IProps) {
const { className } = props;
const roleType = useRoleType(); const roleType = useRoleType();
const currentPage = useSelector(selectHomePageType);
const dispatch = useDispatch();
const tabs = roleType === RoleType.Anchor ? ANCHOR_TAB_LIST : COMPANY_TAB_LIST; const tabs = roleType === RoleType.Anchor ? ANCHOR_TAB_LIST : COMPANY_TAB_LIST;
const handleTabClick = useCallback( const handleTabClick = useCallback(
(value: PageType) => { (value: PageType) => {
if (value === type) { if (value === currentPage) {
return; return;
} }
dispatch(changeHomePage(value));
const item = tabs.find((i: TabItemType) => i.type === value); const item = tabs.find((i: TabItemType) => i.type === value);
log('tab bar changed', value, item?.pagePath); log('tab bar changed', value, item?.pagePath);
item && switchTab(item.pagePath as PageUrl); item && switchTab(item.pagePath as PageUrl);
}, },
[tabs, type] [tabs, currentPage, dispatch]
); );
// useEffect(() => { // useEffect(() => {
@ -66,14 +72,14 @@ function BaseTabBar({ className, type }: IProps) {
return ( return (
<div className={classNames(`${PREFIX}__wrapper`, className)} id={APP_TAB_BAR_ID}> <div className={classNames(`${PREFIX}__wrapper`, className)} id={APP_TAB_BAR_ID}>
<Tabbar className={PREFIX} value={type}> <Tabbar className={PREFIX} defaultValue={currentPage}>
{tabs.map((item: TabItemType) => { {tabs.map((item: TabItemType) => {
return ( return (
<Tabbar.TabItem <Tabbar.TabItem
key={item.pagePath} key={item.pagePath}
value={item.type} value={item.type}
onClick={() => handleTabClick(item.type)} onClick={() => handleTabClick(item.type)}
className={classNames(`${PREFIX}__item`, { selected: item.type === type })} className={classNames(`${PREFIX}__item`, { selected: item.type === currentPage })}
> >
<TabItem item={item} /> <TabItem item={item} />
</Tabbar.TabItem> </Tabbar.TabItem>

View File

@ -1,13 +0,0 @@
import Select, { ISelectProps } from '@/components/select';
import { JOB_TYPE_SELECT_OPTIONS_WITH_ALL, JobType } from '@/constants/job';
interface IProps extends Omit<ISelectProps<JobType>, 'options'> {
value: JobType;
}
function TopCategorySelect(props: IProps) {
const { value: selectValue, onSelect } = props;
return <Select options={JOB_TYPE_SELECT_OPTIONS_WITH_ALL} title="品类" value={selectValue} onSelect={onSelect} />;
}
export default TopCategorySelect;

View File

@ -13,9 +13,9 @@
&__title { &__title {
font-size: 32px; font-size: 32px;
line-height: 48px; line-height: 48px;
margin-top: 32px; font-weight: 500;
font-weight: 400; color: @blColor;
color: #1d2129; margin-top: 24px;
&:first-child { &:first-child {
margin-top: 0; margin-top: 0;
@ -30,21 +30,13 @@
margin-top: 24px; margin-top: 24px;
} }
&__extra {
margin-top: 24px;
font-weight: 400;
font-size: 24px;
line-height: 40px;
color: @blHighlightColor;
}
&__cost-describe { &__cost-describe {
height: 100px; height: 100px;
padding: 0 32px; padding: 0 32px;
border-radius: 16px; border-radius: 16px;
.flex-row(); .flex-row();
justify-content: space-between; justify-content: space-between;
background: #ffffff; background: #FFFFFF;
margin-top: 24px; margin-top: 24px;
&__price { &__price {
@ -72,22 +64,9 @@
line-height: 48px; line-height: 48px;
font-weight: 400; font-weight: 400;
color: @blColorG2; color: @blColorG2;
background: #ffffff; background: #FFFFFF;
border-radius: 16px; border-radius: 16px;
.underline {
display: inline;
font-weight: 500;
font-size: 32px;
line-height: 48px;
color: @blColor;
position: relative;
background-image: linear-gradient(to bottom, rgba(109, 61, 245, 0.6) 0%, rgba(109, 61, 245, 0.6) 100%);
background-repeat: no-repeat;
background-size: 100% 12px;
background-position: bottom;
}
&__describe { &__describe {
.flex-row(); .flex-row();
font-size: 28px; font-size: 28px;
@ -107,16 +86,4 @@
.button(@width: 100%; @height: 80px; @fontSize: 32px); .button(@width: 100%; @height: 80px; @fontSize: 32px);
margin-top: 40px; margin-top: 40px;
} }
&__publish-title {
font-size: 36px;
font-weight: 500;
line-height: 56px;
color: @blColor;
}
&__publish-button {
.button(@width: 360px, @height: 72px, @fontSize: 28px, @fontWeight: 400, @borderRadius: 44px);
margin: 40px auto auto;
}
} }

View File

@ -1,22 +1,17 @@
import { Button } from '@tarojs/components'; import { Button, Image, Text } from '@tarojs/components';
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import { Cell, Dialog } from '@taroify/core'; import { Cell } from '@taroify/core';
import { Fragment, useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useState, useEffect } from 'react';
import PageLoading from '@/components/page-loading'; import PageLoading from '@/components/page-loading';
import { PublishJobQrCodeDialog } from '@/components/product-dialog/publish-job'; import { PublishJobQrCodeDialog } from '@/components/product-dialog/publish-job';
import CompanyPublishJobBuy from '@/components/product-dialog/steps-ui/company-publish-job-buy';
import SafeBottomPadding from '@/components/safe-bottom-padding'; import SafeBottomPadding from '@/components/safe-bottom-padding';
import { ISelectOption, PopupSelect } from '@/components/select'; import { ISelectOption, PopupSelect } from '@/components/select';
import { PageUrl } from '@/constants/app'; import { PageUrl } from '@/constants/app';
import { JobManageStatus } from '@/constants/job'; import { OrderStatus, OrderType, ProductSpecId, ProductType } from '@/constants/product';
import { OrderStatus, OrderType, ProductType } from '@/constants/product'; import { BatchPublishGroup } from '@/types/group';
import useCityOperators from '@/hooks/use-city-operators';
import { usePublishJob } from '@/hooks/use-publish-job';
import { CityConfigListItem } from '@/types/location';
import { logWithPrefix } from '@/utils/common'; import { logWithPrefix } from '@/utils/common';
import { requestJobDetail } from '@/utils/job';
import { import {
getOrderPrice, getOrderPrice,
isCancelPay, isCancelPay,
@ -27,38 +22,71 @@ import {
} from '@/utils/product'; } from '@/utils/product';
import { navigateTo } from '@/utils/route'; import { navigateTo } from '@/utils/route';
import Toast from '@/utils/toast'; import Toast from '@/utils/toast';
import './index.less'; import './index.less';
interface CityOption extends ISelectOption<CityConfigListItem> { interface CityValue extends BatchPublishGroup {
value: CityConfigListItem; cityName: string;
}
interface CityOption extends ISelectOption<CityValue> {
value: CityValue;
} }
const PREFIX = 'user-batch-publish'; const PREFIX = 'user-batch-publish';
const log = logWithPrefix(PREFIX); const log = logWithPrefix(PREFIX);
const SERVICE_ILLUSTRATE = `群发次数每日一次连发3天
群发内容:仅限主播招聘通告,违规内容不发
联系方法:通告中留通告主联系方式,主播直接联系`;
const cityValues: CityValue[] = [
{ cityCode: '440100', cityName: '广州', count: 300 },
{ cityCode: '440300', cityName: '深圳', count: 100 },
{ cityCode: '330100', cityName: '杭州', count: 300 },
{ cityCode: '110100', cityName: '北京', count: 100 },
{ cityCode: '510100', cityName: '成都', count: 50 },
{ cityCode: '430100', cityName: '长沙', count: 50 },
{ cityCode: '350200', cityName: '厦门', count: 50 },
{ cityCode: '310100', cityName: '上海', count: 100 },
{ cityCode: '420100', cityName: '武汉', count: 50 },
{ cityCode: '610100', cityName: '西安', count: 50 },
{ cityCode: '410100', cityName: '郑州', count: 100 },
].sort((a, b) => b.count - a.count);
const MIN_GROUP_SIZE = 20;
const GROUP_OPTIONS = [
{ value: MIN_GROUP_SIZE, productSpecId: ProductSpecId.GroupBatchPublish20, label: '20', price: 18 },
{ value: 50, productSpecId: ProductSpecId.GroupBatchPublish50, label: '50', price: 40 },
{ value: 100, productSpecId: ProductSpecId.GroupBatchPublish100, label: '100', price: 68 },
{ value: 300, productSpecId: ProductSpecId.GroupBatchPublish300, label: '300', price: 128 },
{ value: 500, productSpecId: ProductSpecId.GroupBatchPublish500, label: '500', price: 188 },
{ value: 1000, productSpecId: ProductSpecId.GroupBatchPublish1000, label: '1000', price: 288 },
];
export default function UserBatchPublish({ cityCode, jobId }: { cityCode?: string; jobId?: string }) { const calcPrice = (city: CityValue | null) => {
if (!city) {
return {};
}
const { count } = city;
const originalPrice = count * 1;
const price = GROUP_OPTIONS.find(o => o.value === count)?.price || 18;
const productSpecId = GROUP_OPTIONS.find(o => o.value === count)?.productSpecId || ProductSpecId.GroupBatchPublish20;
return { price, originalPrice, productSpecId };
};
export default function UserBatchPublish() {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [showQrCode, setShowQrCode] = useState(false);
const [selectable, setSelectable] = useState(false);
const [showCitySelect, setShowCitySelect] = useState(false); const [showCitySelect, setShowCitySelect] = useState(false);
const [city, setCity] = useState<CityConfigListItem | null>(null); const [showQrCode, setShowQrCode] = useState(false);
const [showPublishJob, setShowPublishJob] = useState(false); const [city, setCity] = useState<CityOption['value'] | null>(null);
const [cityOptions, setCityOptions] = useState<CityOption[]>([]);
const { price, originalPrice, productSpecId } = calcPrice(city);
const cityOperators = useCityOperators();
const availableCities = useMemo(() => cityOperators.filter(c => c.sendCount), [cityOperators]);
const cityOptions: CityOption[] = useMemo(
() => availableCities.map(value => ({ value, label: value.cityName })),
[availableCities]
);
const [showBuy, setShowBuy, handlePublishJob] = usePublishJob(jobId);
const handleClickCity = useCallback(() => setShowCitySelect(true), []); const handleClickCity = useCallback(() => setShowCitySelect(true), []);
const handleSelectCity = useCallback(value => { const handleSelectCity = useCallback(value => {
setCity(value); setCity(value);
setShowCitySelect(false); setShowCitySelect(false);
}, []); }, []);
const handleClickViewGroup = useCallback(() => navigateTo(PageUrl.GroupList, { city: city?.cityCode }), [city]); const handleClickViewGroup = useCallback(() => navigateTo(PageUrl.GroupList, { city: city?.cityCode }), [city]);
const handleClickBuy = useCallback(async () => { const handleClickBuy = useCallback(async () => {
@ -67,7 +95,7 @@ export default function UserBatchPublish({ cityCode, jobId }: { cityCode?: strin
// setShowQrCode(true); // setShowQrCode(true);
// return; // return;
// } // }
if (!city || !city.payPrice || !city.showPrice || !city.sendCount || !city.productSpecId) { if (!price || !productSpecId) {
return; return;
} }
try { try {
@ -79,23 +107,12 @@ export default function UserBatchPublish({ cityCode, jobId }: { cityCode?: strin
setShowQrCode(true); setShowQrCode(true);
return; return;
} }
if (jobId && cityCode) {
const jobDetail = await requestJobDetail(jobId);
if (jobDetail.status !== JobManageStatus.Open) {
Taro.hideLoading();
setShowPublishJob(true);
return;
}
}
const { payOrderNo, createPayInfo } = await requestCreatePayInfo({ const { payOrderNo, createPayInfo } = await requestCreatePayInfo({
type: OrderType.GroupBatchPublish, type: OrderType.GroupBatchPublish,
amt: city.payPrice, amt: getOrderPrice(price),
// amt: 1, // amt: 1,
productCode: ProductType.GroupBatchPublish, productCode: ProductType.GroupBatchPublish,
productSpecId: city.productSpecId, productSpecId: productSpecId,
}); });
log('handleBuy payInfo', payOrderNo, createPayInfo); log('handleBuy payInfo', payOrderNo, createPayInfo);
await requestPayment({ await requestPayment({
@ -117,27 +134,21 @@ export default function UserBatchPublish({ cityCode, jobId }: { cityCode?: strin
Toast.error(isCancelPay(e) ? '取消购买' : '购买失败请重试'); Toast.error(isCancelPay(e) ? '取消购买' : '购买失败请重试');
log('handleBuy error', e); log('handleBuy error', e);
} }
}, [cityCode, jobId, city]); }, [price, productSpecId]);
useEffect(() => { useEffect(() => {
if (!availableCities.length) {
return;
}
try { try {
const initCity = cityCode ? availableCities.find(o => o.cityCode === cityCode) : availableCities[0]; const cOptions: CityOption[] = cityValues.map(value => ({ value, label: value.cityName }));
const initCity = cOptions[0].value;
setSelectable(!cityCode);
setLoading(false); setLoading(false);
if (initCity) {
setCity(initCity); setCity(initCity);
} else { setCityOptions(cOptions);
Toast.info('当前城市不支持代发'); log('init data done', cOptions);
}
} catch (e) { } catch (e) {
Toast.error('加载失败请重试'); Toast.error('加载失败请重试');
} }
}, [availableCities, cityCode]); }, []);
if (loading) { if (loading) {
return <PageLoading />; return <PageLoading />;
@ -145,43 +156,29 @@ export default function UserBatchPublish({ cityCode, jobId }: { cityCode?: strin
return ( return (
<div className={PREFIX}> <div className={PREFIX}>
{/*<Image mode="widthFix" className={`${PREFIX}__header-image`} src="https://neighbourhood.cn/pubJob.png" />*/} <Image mode="widthFix" className={`${PREFIX}__header-image`} src="https://neighbourhood.cn/pubJob.png" />
<div className={`${PREFIX}__title`}></div>
<div className={`${PREFIX}__illustrate`}>
<div className="underline"></div>
</div>
{selectable ? (
<Fragment>
<div className={`${PREFIX}__title`}></div> <div className={`${PREFIX}__title`}></div>
<Cell isLink align="center" className={`${PREFIX}__cell`} title={city?.cityName} onClick={handleClickCity} /> <Cell isLink align="center" className={`${PREFIX}__cell`} title={city?.cityName} onClick={handleClickCity} />
</Fragment>
) : (
<Fragment>
<div className={`${PREFIX}__title`}></div>
<Cell align="center" className={`${PREFIX}__cell`} title={city ? city.cityName : '暂不支持代发'} />
</Fragment>
)}
{city && (
<Fragment>
<div className={`${PREFIX}__extra`} onClick={handleClickViewGroup}>
</div>
<div className={`${PREFIX}__title`}></div> <div className={`${PREFIX}__title`}></div>
<Cell align="center" className={`${PREFIX}__cell`} title={city?.sendCount} /> <Cell align="center" className={`${PREFIX}__cell`} title={city?.count} />
<div className={`${PREFIX}__title`}></div> <div className={`${PREFIX}__title`}></div>
<div className={`${PREFIX}__cost-describe`}> <div className={`${PREFIX}__cost-describe`}>
<div className={`${PREFIX}__cost-describe__price`}>{`${city?.showPrice}`}</div> <div className={`${PREFIX}__cost-describe__price`}>{`${price}`}</div>
<div <div className={`${PREFIX}__cost-describe__original_price`}>{`原价:${originalPrice}`}</div>
className={`${PREFIX}__cost-describe__original_price`} </div>
>{`原价:${city?.originalPrice || city?.sendCount}`}</div> <div className={`${PREFIX}__title`}></div>
<div className={`${PREFIX}__illustrate`}>
<Text>{SERVICE_ILLUSTRATE}</Text>
<div className={`${PREFIX}__illustrate__describe`}>
<div></div>
<div className={`${PREFIX}__illustrate__describe__view`} onClick={handleClickViewGroup}>
</div>
</div>
</div> </div>
<Button className={`${PREFIX}__buy-button`} onClick={handleClickBuy}> <Button className={`${PREFIX}__buy-button`} onClick={handleClickBuy}>
</Button> </Button>
</Fragment>
)}
<SafeBottomPadding /> <SafeBottomPadding />
<div> <div>
<PopupSelect <PopupSelect
@ -192,19 +189,6 @@ export default function UserBatchPublish({ cityCode, jobId }: { cityCode?: strin
onClose={() => setShowCitySelect(false)} onClose={() => setShowCitySelect(false)}
/> />
<PublishJobQrCodeDialog onClose={() => setShowQrCode(false)} open={showQrCode} /> <PublishJobQrCodeDialog onClose={() => setShowQrCode(false)} open={showQrCode} />
<Dialog open={showBuy} onClose={() => setShowBuy(false)}>
<Dialog.Content>
<CompanyPublishJobBuy onNext={handlePublishJob} />
</Dialog.Content>
</Dialog>
<Dialog open={showPublishJob} onClose={() => setShowPublishJob(false)}>
<Dialog.Content>
<div className={`${PREFIX}__publish-title`}></div>
<Button className={`${PREFIX}__publish-button`} onClick={handlePublishJob}>
</Button>
</Dialog.Content>
</Dialog>
</div> </div>
</div> </div>
); );

View File

@ -17,7 +17,6 @@ export enum EventName {
ADD_GROUP = 'add_group', ADD_GROUP = 'add_group',
SELECT_CITY = 'select_city', SELECT_CITY = 'select_city',
CREATE_PROFILE = 'create_profile', CREATE_PROFILE = 'create_profile',
READ_CONTACT = 'read_contact',
UPDATE_PROFILE = 'update_profile', UPDATE_PROFILE = 'update_profile',
EDIT_JOB_DESCRIBE = 'edit_job_describe', EDIT_JOB_DESCRIBE = 'edit_job_describe',
JOB_UPDATE = 'job_update', JOB_UPDATE = 'job_update',
@ -25,7 +24,6 @@ export enum EventName {
SELECT_MY_PUBLISH_JOB = 'select_my_publish_job', SELECT_MY_PUBLISH_JOB = 'select_my_publish_job',
EXIT_CHAT_PAGE = 'exit_chat_page', EXIT_CHAT_PAGE = 'exit_chat_page',
VIEW_MATERIAL_SUCCESS = 'view_material_success', VIEW_MATERIAL_SUCCESS = 'view_material_success',
VIEW_JOB_SUCCESS = 'view_job_success',
} }
export enum OpenSource { export enum OpenSource {
@ -36,8 +34,6 @@ export enum OpenSource {
UserPage = 'user_page', UserPage = 'user_page',
AnchorPage = 'anchor_page', AnchorPage = 'anchor_page',
MaterialViewPage = 'material_view_page', MaterialViewPage = 'material_view_page',
GroupOwnerCertificate = 'group_owner_certificate',
InviteOperations = 'invite_operations',
} }
export enum PageUrl { export enum PageUrl {
@ -78,13 +74,6 @@ export enum PageUrl {
PrivacyWebview = 'pages/privacy-webview/index', PrivacyWebview = 'pages/privacy-webview/index',
Partner = 'pages/partner/index', Partner = 'pages/partner/index',
WithdrawRecord = 'pages/withdraw-record/index', WithdrawRecord = 'pages/withdraw-record/index',
GroupDelegatePublish = 'pages/group-delegate-publish/index',
AccelerateDelegatePublish = 'pages/accelerate-delegate-publish/index',
GiveVip = 'pages/give-vip/index',
GroupOwnerCertificate = 'pages/group-owner-certification/index',
PartnerShareVip = 'pages/partner-share-vip/index',
InviteOperations = 'pages/invite-operations/index',
GroupDetail = 'pages/group-detail/index',
} }
export enum PluginUrl { export enum PluginUrl {

View File

@ -10,8 +10,4 @@ export enum CacheKey {
LAST_SELECT_MY_JOB = '__last_select_my_job__', LAST_SELECT_MY_JOB = '__last_select_my_job__',
CLOSE_PARTNER_BANNER = '__close_partner_banner__', CLOSE_PARTNER_BANNER = '__close_partner_banner__',
INVITE_CODE = '__invite_code__', INVITE_CODE = '__invite_code__',
AGREEMENT_SIGNED = '__agreement_signed__',
CITY_CODES = '__city_codes__',
JOIN_GROUP_CARD_CLICKED = '__join_group_card_clicked__',
JOIN_GROUP_POPUP_CLICKED = '__join_group_popup_clicked__',
} }

View File

@ -5861,16 +5861,6 @@ export const CITY_INDEXES_LIST = [
]; ];
export const GROUP_CITY_INDEXES_LIST = [ export const GROUP_CITY_INDEXES_LIST = [
{
letter: 'B',
data: [
{
cityCode: '110100',
cityName: '北京',
keyword: 'BEIJING北京',
},
],
},
{ {
letter: 'C', letter: 'C',
data: [ data: [
@ -5884,11 +5874,6 @@ export const GROUP_CITY_INDEXES_LIST = [
cityName: '成都', cityName: '成都',
keyword: 'CHENGDOU成都', keyword: 'CHENGDOU成都',
}, },
{
cityCode: '430100',
cityName: '长沙',
keyword: 'CHANGSHA长沙',
},
], ],
}, },
{ {
@ -5929,16 +5914,6 @@ export const GROUP_CITY_INDEXES_LIST = [
{ {
letter: 'H', letter: 'H',
data: [ data: [
{
cityCode: '330100',
cityName: '杭州',
keyword: 'HANGZHOU杭州',
},
{
cityCode: '230100',
cityName: '哈尔滨',
keyword: 'HAERBIN哈尔滨',
},
{ {
cityCode: '340100', cityCode: '340100',
cityName: '合肥', cityName: '合肥',
@ -5946,26 +5921,6 @@ export const GROUP_CITY_INDEXES_LIST = [
}, },
], ],
}, },
{
letter: 'J',
data: [
{
cityCode: '370100',
cityName: '济南',
keyword: 'JINAN济南',
},
],
},
{
letter: 'K',
data: [
{
cityCode: '530100',
cityName: '昆明',
keyword: 'KUNMING昆明',
},
],
},
{ {
letter: 'N', letter: 'N',
data: [ data: [
@ -5984,11 +5939,6 @@ export const GROUP_CITY_INDEXES_LIST = [
cityName: '青岛', cityName: '青岛',
keyword: 'QINGDAO青岛', keyword: 'QINGDAO青岛',
}, },
{
cityCode: '350500',
cityName: '泉州',
keyword: 'QUANZHOU泉州',
},
], ],
}, },
{ {
@ -5999,26 +5949,21 @@ export const GROUP_CITY_INDEXES_LIST = [
cityName: '上海', cityName: '上海',
keyword: 'SHANGHAI上海', keyword: 'SHANGHAI上海',
}, },
{
cityCode: '210100',
cityName: '沈阳',
keyword: 'SHENYANG沈阳',
},
{ {
cityCode: '440300', cityCode: '440300',
cityName: '深圳', cityName: '深圳',
keyword: 'SHENZHEN深圳', keyword: 'SHENZHEN深圳',
}, },
{
cityCode: '130100',
cityName: '石家庄',
keyword: 'SHIJIAZHUANG石家庄',
},
{ {
cityCode: '320500', cityCode: '320500',
cityName: '苏州', cityName: '苏州',
keyword: 'SUZHOU苏州', keyword: 'SUZHOU苏州',
}, },
{
cityCode: '330300',
cityName: '温州',
keyword: 'WENZHOU温州',
},
], ],
}, },
{ {
@ -6029,21 +5974,11 @@ export const GROUP_CITY_INDEXES_LIST = [
cityName: '天津', cityName: '天津',
keyword: 'TIANJIN天津', keyword: 'TIANJIN天津',
}, },
{
cityCode: '140100',
cityName: '太原',
keyword: 'TAIYUAN太原',
},
], ],
}, },
{ {
letter: 'W', letter: 'W',
data: [ data: [
{
cityCode: '330300',
cityName: '温州',
keyword: 'WENZHOU温州',
},
{ {
cityCode: '420100', cityCode: '420100',
cityName: '武汉', cityName: '武汉',
@ -6059,11 +5994,6 @@ export const GROUP_CITY_INDEXES_LIST = [
cityName: '西安', cityName: '西安',
keyword: 'XIAN西安', keyword: 'XIAN西安',
}, },
{
cityCode: '350200',
cityName: '厦门',
keyword: 'XIAMEN厦门',
},
], ],
}, },
{ {
@ -6074,11 +6004,6 @@ export const GROUP_CITY_INDEXES_LIST = [
cityName: '郑州', cityName: '郑州',
keyword: 'ZHENGZHOU郑州', keyword: 'ZHENGZHOU郑州',
}, },
{
cityCode: '430200',
cityName: '株洲',
keyword: 'ZHUZHOU株洲',
},
], ],
}, },
]; ];

View File

@ -56,11 +56,5 @@ export const GROUPS: GroupItem[] = [
{ title: '【温州】', cityCode: 330300, serviceUrl: 'https://work.weixin.qq.com/kfid/kfcb0ea5f197a18b335' }, { title: '【温州】', cityCode: 330300, serviceUrl: 'https://work.weixin.qq.com/kfid/kfcb0ea5f197a18b335' },
{ title: '【天津】', cityCode: 120100, serviceUrl: 'https://work.weixin.qq.com/kfid/kfcda46c23dade6f6a3' }, { title: '【天津】', cityCode: 120100, serviceUrl: 'https://work.weixin.qq.com/kfid/kfcda46c23dade6f6a3' },
{ title: '【昆明】', cityCode: 530100, serviceUrl: 'https://work.weixin.qq.com/kfid/kfcf2aebcbf3d46d9cd' }, { title: '【昆明】', cityCode: 530100, serviceUrl: 'https://work.weixin.qq.com/kfid/kfcf2aebcbf3d46d9cd' },
{ title: '【石家庄】', cityCode: 130100, serviceUrl: 'https://work.weixin.qq.com/kfid/kfc7dedcc69aaa9cdae' },
{ title: '【太原】', cityCode: 140100, serviceUrl: 'https://work.weixin.qq.com/kfid/kfccc8b59411baa7c24' },
{ title: '【株洲】', cityCode: 430200, serviceUrl: 'https://work.weixin.qq.com/kfid/kfc8584f903f9263f85' },
{ title: '【哈尔滨】', cityCode: 230100, serviceUrl: 'https://work.weixin.qq.com/kfid/kfcaf757d0590d2bad0' },
{ title: '【沈阳】', cityCode: 210100, serviceUrl: 'https://work.weixin.qq.com/kfid/kfcbe96e71d87061f88' },
{ title: '【济南】', cityCode: 370100, serviceUrl: 'https://work.weixin.qq.com/kfid/kfc57d4d9bd032fe89c' },
{ title: '【全国】', cityCode: 440300, serviceUrl: 'https://work.weixin.qq.com/kfid/kfcc60ac7b6420787a8' }, { title: '【全国】', cityCode: 440300, serviceUrl: 'https://work.weixin.qq.com/kfid/kfcc60ac7b6420787a8' },
]; ];

View File

@ -7,7 +7,6 @@ export enum JobType {
Jewelry = 'JEWELRY', // 珠宝 Jewelry = 'JEWELRY', // 珠宝
Appliance = 'APPLIANCE', // 家电 Appliance = 'APPLIANCE', // 家电
Furniture = 'FURNITURE', // 日用家具 Furniture = 'FURNITURE', // 日用家具
OutdoorSports = 'OUTDOOR_SPORTS',
PetFamily = 'PET_FAMILY', // 母婴宠物 PetFamily = 'PET_FAMILY', // 母婴宠物
Luxury = 'LUXURY', // 奢品 Luxury = 'LUXURY', // 奢品
LocalLive = 'LOCAL_LIVE', // 本地生活 LocalLive = 'LOCAL_LIVE', // 本地生活
@ -22,12 +21,6 @@ export enum EmployType {
Part = 'PARTY_TIME', Part = 'PARTY_TIME',
} }
export enum JobSourceType {
All = 'All',
VX = 'vx',
BL = 'bl',
}
export enum SortType { export enum SortType {
RECOMMEND = 'RECOMMEND', RECOMMEND = 'RECOMMEND',
DISTANCE = 'DISTANCE', DISTANCE = 'DISTANCE',
@ -73,12 +66,6 @@ export const EMPLOY_TYPE_TITLE_MAP = {
[EmployType.Full]: '全职', [EmployType.Full]: '全职',
}; };
export const JOB_SOURCE_TYPE_TITLE_MAP = {
[JobSourceType.All]: '全部',
[JobSourceType.BL]: '急招',
[JobSourceType.VX]: '群通告',
};
export const JOB_TYPE_TITLE_MAP: { [key in JobType]: string } = { export const JOB_TYPE_TITLE_MAP: { [key in JobType]: string } = {
[JobType.All]: '全部', [JobType.All]: '全部',
[JobType.Finery]: '服饰', [JobType.Finery]: '服饰',
@ -88,7 +75,6 @@ export const JOB_TYPE_TITLE_MAP: { [key in JobType]: string } = {
[JobType.Jewelry]: '珠宝', [JobType.Jewelry]: '珠宝',
[JobType.Appliance]: '家电', [JobType.Appliance]: '家电',
[JobType.Furniture]: '日用家具', [JobType.Furniture]: '日用家具',
[JobType.OutdoorSports]: '户外运动',
[JobType.PetFamily]: '母婴宠物', [JobType.PetFamily]: '母婴宠物',
[JobType.Luxury]: '奢品', [JobType.Luxury]: '奢品',
[JobType.LocalLive]: '本地生活', [JobType.LocalLive]: '本地生活',
@ -115,31 +101,16 @@ export const JOB_MANAGE_STATUS_TITLE_MAP = {
export const JOB_PAGE_TABS = [ export const JOB_PAGE_TABS = [
{ {
type: JobSourceType.All, type: EmployType.All,
title: JOB_SOURCE_TYPE_TITLE_MAP[JobSourceType.All], title: EMPLOY_TYPE_TITLE_MAP[EmployType.All],
}, },
{ {
type: JobSourceType.BL, type: EmployType.Part,
title: JOB_SOURCE_TYPE_TITLE_MAP[JobSourceType.BL], title: EMPLOY_TYPE_TITLE_MAP[EmployType.Part],
},
// {
// type: JobSourceType.VX,
// title: JOB_SOURCE_TYPE_TITLE_MAP[JobSourceType.VX],
// },
];
export const JOB_EMPLOY_TYPE_OPTIONS = [
{
value: EmployType.All,
label: EMPLOY_TYPE_TITLE_MAP[EmployType.All],
}, },
{ {
value: EmployType.Part, type: EmployType.Full,
label: EMPLOY_TYPE_TITLE_MAP[EmployType.Part], title: EMPLOY_TYPE_TITLE_MAP[EmployType.Full],
},
{
value: EmployType.Full,
label: EMPLOY_TYPE_TITLE_MAP[EmployType.Full],
}, },
]; ];
@ -175,7 +146,6 @@ export const JOB_TYPE_SELECT_OPTIONS = [
{ label: JOB_TYPE_TITLE_MAP[JobType.Jewelry], value: JobType.Jewelry }, { label: JOB_TYPE_TITLE_MAP[JobType.Jewelry], value: JobType.Jewelry },
{ label: JOB_TYPE_TITLE_MAP[JobType.Appliance], value: JobType.Appliance }, { label: JOB_TYPE_TITLE_MAP[JobType.Appliance], value: JobType.Appliance },
{ label: JOB_TYPE_TITLE_MAP[JobType.Furniture], value: JobType.Furniture }, { label: JOB_TYPE_TITLE_MAP[JobType.Furniture], value: JobType.Furniture },
{ label: JOB_TYPE_TITLE_MAP[JobType.OutdoorSports], value: JobType.OutdoorSports },
{ label: JOB_TYPE_TITLE_MAP[JobType.PetFamily], value: JobType.PetFamily }, { label: JOB_TYPE_TITLE_MAP[JobType.PetFamily], value: JobType.PetFamily },
{ label: JOB_TYPE_TITLE_MAP[JobType.Luxury], value: JobType.Luxury }, { label: JOB_TYPE_TITLE_MAP[JobType.Luxury], value: JobType.Luxury },
{ label: JOB_TYPE_TITLE_MAP[JobType.LocalLive], value: JobType.LocalLive }, { label: JOB_TYPE_TITLE_MAP[JobType.LocalLive], value: JobType.LocalLive },
@ -184,11 +154,6 @@ export const JOB_TYPE_SELECT_OPTIONS = [
{ label: JOB_TYPE_TITLE_MAP[JobType.Other], value: JobType.Other }, { label: JOB_TYPE_TITLE_MAP[JobType.Other], value: JobType.Other },
]; ];
export const JOB_TYPE_SELECT_OPTIONS_WITH_ALL = [
{ label: JOB_TYPE_TITLE_MAP[JobType.All], value: JobType.All },
...JOB_TYPE_SELECT_OPTIONS,
];
const MAX_SALARY = 10000000; const MAX_SALARY = 10000000;
export const PART_EMPLOY_SALARY_OPTIONS = [ export const PART_EMPLOY_SALARY_OPTIONS = [
{ label: '不限' }, { label: '不限' },
@ -209,10 +174,3 @@ export const FULL_EMPLOY_SALARY_OPTIONS = [
export const PART_PRICE_OPTIONS = PART_EMPLOY_SALARY_OPTIONS.filter(o => !!o.value); export const PART_PRICE_OPTIONS = PART_EMPLOY_SALARY_OPTIONS.filter(o => !!o.value);
export const FULL_PRICE_OPTIONS = FULL_EMPLOY_SALARY_OPTIONS.filter(o => !!o.value); export const FULL_PRICE_OPTIONS = FULL_EMPLOY_SALARY_OPTIONS.filter(o => !!o.value);
export enum GET_CONTACT_TYPE {
VIP = 'vip',
MATERIAL = 'material',
GROUP = 'group',
INVITE = 'invite',
}

View File

@ -3,7 +3,6 @@ export enum ProfileGroupType {
Intention = 'intention', Intention = 'intention',
Experience = 'experience', Experience = 'experience',
Advantages = 'advantages', Advantages = 'advantages',
Others = 'others',
} }
export const ProfileTitleMap = { export const ProfileTitleMap = {
@ -11,7 +10,6 @@ export const ProfileTitleMap = {
[ProfileGroupType.Intention]: '求职意向', [ProfileGroupType.Intention]: '求职意向',
[ProfileGroupType.Experience]: '直播经验', [ProfileGroupType.Experience]: '直播经验',
[ProfileGroupType.Advantages]: '自身优势', [ProfileGroupType.Advantages]: '自身优势',
[ProfileGroupType.Others]: '身型、学历',
}; };
export enum WorkedYears { export enum WorkedYears {
@ -21,13 +19,6 @@ export enum WorkedYears {
MoreThreeYear = 3, MoreThreeYear = 3,
} }
export enum EducationType {
MiddleSchool = 1,
HighSchool = 2,
AssociateDegree = 3,
BachelorOrAbove = 4,
}
export enum GenderType { export enum GenderType {
All = -1, All = -1,
MEN = 0, MEN = 0,
@ -93,25 +84,14 @@ export const WORK_YEAR_OPTIONS = [
{ label: WORK_YEAR_LABELS[WorkedYears.MoreThreeYear], value: WorkedYears.MoreThreeYear }, { label: WORK_YEAR_LABELS[WorkedYears.MoreThreeYear], value: WorkedYears.MoreThreeYear },
]; ];
export const ALL_GENDER_TYPES = [GenderType.All, GenderType.MEN, GenderType.WOMEN];
export const GENDER_TYPE_TITLE_MAP = { export const GENDER_TYPE_TITLE_MAP = {
[GenderType.All]: '不限', [GenderType.All]: '不限',
[GenderType.WOMEN]: '女', [GenderType.WOMEN]: '女',
[GenderType.MEN]: '男', [GenderType.MEN]: '男',
}; };
export const GENDER_OPTIONS = [
{
value: GenderType.All,
label: GENDER_TYPE_TITLE_MAP[GenderType.All],
},
{
value: GenderType.WOMEN,
label: GENDER_TYPE_TITLE_MAP[GenderType.WOMEN],
},
{
value: GenderType.MEN,
label: GENDER_TYPE_TITLE_MAP[GenderType.MEN],
},
];
export const ALL_ANCHOR_READ_TYPES = Object.values(AnchorReadType); export const ALL_ANCHOR_READ_TYPES = Object.values(AnchorReadType);
export const ANCHOR_READ_TITLE_MAP = { export const ANCHOR_READ_TITLE_MAP = {
@ -119,15 +99,3 @@ export const ANCHOR_READ_TITLE_MAP = {
[AnchorReadType.Read]: '已读', [AnchorReadType.Read]: '已读',
[AnchorReadType.Unread]: '未读', [AnchorReadType.Unread]: '未读',
}; };
export const EDUCATION_TYPE_LABELS = {
[EducationType.MiddleSchool]: '初中',
[EducationType.HighSchool]: '职高/高中',
[EducationType.AssociateDegree]: '大专',
[EducationType.BachelorOrAbove]: '本科及以上',
};
export const EDUCATION_TYPE_OPTIONS = [
{ label: EDUCATION_TYPE_LABELS[EducationType.MiddleSchool], value: EducationType.MiddleSchool },
{ label: EDUCATION_TYPE_LABELS[EducationType.HighSchool], value: EducationType.HighSchool },
{ label: EDUCATION_TYPE_LABELS[EducationType.AssociateDegree], value: EducationType.AssociateDegree },
{ label: EDUCATION_TYPE_LABELS[EducationType.BachelorOrAbove], value: EducationType.BachelorOrAbove },
];

View File

@ -52,9 +52,3 @@ export const ProfitStatusDescriptions = {
OTHER: '', OTHER: '',
FINISHED: '已分账', FINISHED: '已分账',
}; };
export const WithdrawStatusDescriptions = {
0: '提现中',
1: '已提现',
2: '失败',
};

View File

@ -27,23 +27,14 @@ export enum ProductSpecId {
AddGroup2 = 'ADDGROUP_2', AddGroup2 = 'ADDGROUP_2',
AddGroup3 = 'ADDGROUP_3', AddGroup3 = 'ADDGROUP_3',
BossVip = 'BOSSVIP', BossVip = 'BOSSVIP',
DailyVIP = 'VIP_D',
WeeklyVIP = 'VIP_W', WeeklyVIP = 'VIP_W',
MonthlyVIP = 'VIP_M', // 30 每天十次 MonthlyVIP = 'VIP_M', // 30 每天十次
NewMonthlyVIP = 'VIP_M_NEW', // 18 每天五次 NewMonthlyVIP = 'VIP_M_NEW', // 18 每天五次
GroupBatchPublish20 = 'GROUP_BATCH_PUSH_20', GroupBatchPublish20 = 'GROUP_BATCH_PUSH_20',
GroupBatchPublish50 = 'GROUP_BATCH_PUSH_50', GroupBatchPublish50 = 'GROUP_BATCH_PUSH_50',
GroupBatchPublish60 = 'GROUP_BATCH_PUSH_60',
GroupBatchPublish80 = 'GROUP_BATCH_PUSH_80',
GroupBatchPublish100 = 'GROUP_BATCH_PUSH_100', GroupBatchPublish100 = 'GROUP_BATCH_PUSH_100',
GroupBatchPublish120 = 'GROUP_BATCH_PUSH_120',
GroupBatchPublish150 = 'GROUP_BATCH_PUSH_150',
GroupBatchPublish200 = 'GROUP_BATCH_PUSH_200',
GroupBatchPublish250 = 'GROUP_BATCH_PUSH_250',
GroupBatchPublish300 = 'GROUP_BATCH_PUSH_300', GroupBatchPublish300 = 'GROUP_BATCH_PUSH_300',
GroupBatchPublish500 = 'GROUP_BATCH_PUSH_500', GroupBatchPublish500 = 'GROUP_BATCH_PUSH_500',
GroupBatchPublish750 = 'GROUP_BATCH_PUSH_750',
GroupBatchPublish800 = 'GROUP_BATCH_PUSH_800',
GroupBatchPublish1000 = 'GROUP_BATCH_PUSH_1000', GroupBatchPublish1000 = 'GROUP_BATCH_PUSH_1000',
BOSS_PUB_JOB_1 = 'BOSS_PUB_JOB_1', // 旧版企业发通告会员 BOSS_PUB_JOB_1 = 'BOSS_PUB_JOB_1', // 旧版企业发通告会员
BOSS_VIP_NEW_1 = 'BOSS_VIP_NEW_1', // 新版企业发通告会员 - 周 BOSS_VIP_NEW_1 = 'BOSS_VIP_NEW_1', // 新版企业发通告会员 - 周

View File

@ -1,6 +1,6 @@
export enum SubscribeTempId { export enum SubscribeTempId {
INTERVIEW_INVITATION = 'obPJO1nmXoSwAovbuuUz4aVfb6Ir8AOp1kY_JlQ18dY', INTERVIEW_INVITATION = 'obPJO1nmXoSwAovbuuUz4aVfb6Ir8AOp1kY_JlQ18dY',
UNREAD_MESSAGE_REMINDER = '5QcNcHdNnWpMvNOUwbazYlWv0VXXAzdF87L7c7vVq2s', UNREAD_MESSAGE_REMINDER = 'm-Zx9zgf5KGnlHgxbxSkEtpd7XXCg1r1adC2UZS7sXs',
NEW_MESSAGE_REMINDER = '4lkxb_mDLbJ889PZqunxP5U8auvQHS6hZgzfxgGHo9U', NEW_MESSAGE_REMINDER = '4lkxb_mDLbJ889PZqunxP5U8auvQHS6hZgzfxgGHo9U',
SUBSCRIBE_JOB = 'PYekheJ60PA53SB51wFjySYjMUQjS17elPNnLwoIEQM', SUBSCRIBE_JOB = 'PYekheJ60PA53SB51wFjySYjMUQjS17elPNnLwoIEQM',
SUBSCRIBE_VIP = 'TxQSUxuB9av1bkWudq-UcM1ey9ChVYqTSC9qBPpW0e8', SUBSCRIBE_VIP = 'TxQSUxuB9av1bkWudq-UcM1ey9ChVYqTSC9qBPpW0e8',

View File

@ -54,7 +54,6 @@
flex: 1 0; flex: 1 0;
} }
&__employType-select,
&__salary-select, &__salary-select,
&__search-city { &__search-city {
display: flex; display: flex;

View File

@ -2,44 +2,31 @@ import { Image } from '@tarojs/components';
import { NodesRef, useDidHide } from '@tarojs/taro'; import { NodesRef, useDidHide } from '@tarojs/taro';
import { Tabs } from '@taroify/core'; import { Tabs } from '@taroify/core';
import { ArrowDown, ArrowUp } from '@taroify/icons'; import { ArrowUp, ArrowDown } from '@taroify/icons';
import classNames from 'classnames'; import classNames from 'classnames';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import EmployTypeSelect from '@/components/employ-type-select';
import JobList, { IJobListProps } from '@/components/job-list'; import JobList, { IJobListProps } from '@/components/job-list';
import Overlay from '@/components/overlay'; import Overlay from '@/components/overlay';
// import PartnerBanner from '@/components/partner-banner'; import PartnerBanner from '@/components/partner-banner';
import SalarySelect from '@/components/salary-select'; import SalarySelect from '@/components/salary-select';
import SearchInput from '@/components/search'; import SearchInput from '@/components/search';
import { APP_TAB_BAR_ID, PageUrl } from '@/constants/app'; import { APP_TAB_BAR_ID, PageUrl } from '@/constants/app';
import { CITY_CODE_TO_NAME_MAP } from '@/constants/city'; import { CITY_CODE_TO_NAME_MAP } from '@/constants/city';
import { import { JOB_TABS, JobType, EmployType, ALL_SORT_TYPES, SORT_TYPE_TITLE_MAP, SortType } from '@/constants/job';
ALL_SORT_TYPES,
EMPLOY_TYPE_TITLE_MAP,
EmployType,
JOB_TABS,
JobSourceType,
JobType,
SORT_TYPE_TITLE_MAP,
SortType,
} from '@/constants/job';
import useListHeight, { IUseListHeightProps } from '@/hooks/use-list-height'; import useListHeight, { IUseListHeightProps } from '@/hooks/use-list-height';
import { SalaryRange } from '@/types/job'; import { SalaryRange } from '@/types/job';
import { Coordinate } from '@/types/location'; import { Coordinate } from '@/types/location';
import { logWithPrefix } from '@/utils/common'; import { logWithPrefix } from '@/utils/common';
import { navigateTo } from '@/utils/route'; import { navigateTo } from '@/utils/route';
import './index.less'; import './index.less';
import InviteOperationsBanner from '@/components/invite-operations-banner';
interface IProps { interface IProps {
cityCode: string; cityCode: string;
sortType: SortType; sortType: SortType;
sourceType: JobSourceType;
employType: EmployType; employType: EmployType;
coordinate: Coordinate; coordinate: Coordinate;
onClickCity: () => void; onClickCity: () => void;
onClickEmployType: (type: EmployType) => void;
onClickSort: (type: SortType) => void; onClickSort: (type: SortType) => void;
} }
@ -87,35 +74,20 @@ function ListWrapper(props: IJobListProps) {
} }
function JobFragment(props: IProps) { function JobFragment(props: IProps) {
const { const { cityCode, employType, sortType = SortType.RECOMMEND, coordinate, onClickCity, onClickSort } = props;
cityCode,
employType,
sourceType,
sortType = SortType.RECOMMEND,
coordinate,
onClickEmployType,
onClickCity,
onClickSort,
} = props;
const listHeight = useListHeight(CALC_LIST_PROPS); const listHeight = useListHeight(CALC_LIST_PROPS);
const [tabType, setTabType] = useState<JobType>(JobType.All); const [tabType, setTabType] = useState<JobType>(JobType.All);
const [salaryRange, setSalaryRange] = useState<SalaryRange | undefined>(); const [salaryRange, setSalaryRange] = useState<SalaryRange | undefined>();
const [showSalarySelect, setShowSalarySelect] = useState<boolean>(false); const [showSalarySelect, setShowSalarySelect] = useState<boolean>(false);
const [showEmployTypeSelect, setShowEmployTypeSelect] = useState<boolean>(false);
const { latitude, longitude } = coordinate; const { latitude, longitude } = coordinate;
const handleClickSearch = useCallback(() => navigateTo(PageUrl.JobSearch, { city: cityCode }), [cityCode]); const handleClickSearch = useCallback(() => navigateTo(PageUrl.JobSearch, { city: cityCode }), [cityCode]);
const handleClickSalarySelect = useCallback(() => { const handleClickSalarySelect = useCallback(() => {
setShowSalarySelect(s => !s); setShowSalarySelect(!showSalarySelect);
}, []); }, [showSalarySelect]);
const handleClickEmployTypeSelect = useCallback(() => {
setShowEmployTypeSelect(s => !s);
}, []);
const handleHideSalarySelect = useCallback(() => setShowSalarySelect(false), []); const handleHideSalarySelect = useCallback(() => setShowSalarySelect(false), []);
const handleHideEmployTypeSelect = useCallback(() => setShowEmployTypeSelect(false), []);
const handleSelectSalary = useCallback((value?: SalaryRange) => { const handleSelectSalary = useCallback((value?: SalaryRange) => {
log('handleSelectSalary', value); log('handleSelectSalary', value);
@ -123,14 +95,6 @@ function JobFragment(props: IProps) {
setShowSalarySelect(false); setShowSalarySelect(false);
}, []); }, []);
const handleSelectEmployType = useCallback(
(value?: EmployType) => {
setShowEmployTypeSelect(false);
onClickEmployType(value!);
},
[onClickEmployType]
);
const onTypeChange = useCallback( const onTypeChange = useCallback(
value => { value => {
log('onTypeChange', value); log('onTypeChange', value);
@ -145,12 +109,6 @@ function JobFragment(props: IProps) {
<div className={`${PREFIX}__container`}> <div className={`${PREFIX}__container`}>
<div className={`${PREFIX}__top-search-bar`}> <div className={`${PREFIX}__top-search-bar`}>
<SearchInput disabled className={`${PREFIX}__search`} placeholder="试试 女装" onClick={handleClickSearch} /> <SearchInput disabled className={`${PREFIX}__search`} placeholder="试试 女装" onClick={handleClickSearch} />
<div className={classNames(`${PREFIX}__employType-select`)} onClick={handleClickEmployTypeSelect}>
<div className="title">
{employType && employType !== EmployType.All ? EMPLOY_TYPE_TITLE_MAP[employType] : '类型'}
</div>
{showEmployTypeSelect ? <ArrowUp /> : <ArrowDown />}
</div>
<div className={classNames(`${PREFIX}__search-city`)} onClick={onClickCity}> <div className={classNames(`${PREFIX}__search-city`)} onClick={onClickCity}>
<div className="title">{CITY_CODE_TO_NAME_MAP.get(cityCode)}</div> <div className="title">{CITY_CODE_TO_NAME_MAP.get(cityCode)}</div>
<ArrowDown /> <ArrowDown />
@ -174,15 +132,13 @@ function JobFragment(props: IProps) {
<Tabs className={`${PREFIX}__type-tabs`} value={tabType} onChange={onTypeChange}> <Tabs className={`${PREFIX}__type-tabs`} value={tabType} onChange={onTypeChange}>
{JOB_TABS.map(tab => ( {JOB_TABS.map(tab => (
<Tabs.TabPane title={tab.title} key={tab.type} value={tab.type}> <Tabs.TabPane title={tab.title} key={tab.type} value={tab.type}>
{/*<PartnerBanner />*/} <PartnerBanner />
<InviteOperationsBanner />
<ListWrapper <ListWrapper
category={tab.type} category={tab.type}
cityCode={cityCode} cityCode={cityCode}
sortType={sortType} sortType={sortType}
latitude={latitude} latitude={latitude}
longitude={longitude} longitude={longitude}
sourceType={sourceType}
employType={employType} employType={employType}
minSalary={salaryRange?.minSalary} minSalary={salaryRange?.minSalary}
maxSalary={salaryRange?.maxSalary} maxSalary={salaryRange?.maxSalary}
@ -201,15 +157,6 @@ function JobFragment(props: IProps) {
> >
<SalarySelect type={employType} value={salaryRange} onSelect={handleSelectSalary} /> <SalarySelect type={employType} value={salaryRange} onSelect={handleSelectSalary} />
</Overlay> </Overlay>
<Overlay
visible={showEmployTypeSelect}
onClickOuter={handleHideEmployTypeSelect}
outerClassName={`${PREFIX}__overlay-outer`}
innerClassName={`${PREFIX}__overlay-inner`}
>
<EmployTypeSelect value={employType} onSelect={handleSelectEmployType} />
</Overlay>
</div> </div>
); );
} }

View File

@ -23,7 +23,9 @@ function ProfileBasicFragment(props: IProps, ref) {
const [name, setName] = useState(profile.name || ''); const [name, setName] = useState(profile.name || '');
const [gender, setGender] = useState<GenderType>(profile.gender || GenderType.WOMEN); const [gender, setGender] = useState<GenderType>(profile.gender || GenderType.WOMEN);
const [age, setAge] = useState(profile.age || ''); const [age, setAge] = useState(profile.age || '');
const [workedSecCategoryStr, setWorkedSecCategoryStr] = useState(profile.workedSecCategoryStr || ''); const [height, setHeight] = useState(profile.height || '');
const [weight, setWeight] = useState(profile.weight || '');
const [shoeSize, setShoeSize] = useState(profile.shoeSize || '');
const handleInputName = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => { const handleInputName = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || ''; const value = e.detail.value || '';
@ -38,21 +40,41 @@ function ProfileBasicFragment(props: IProps, ref) {
setAge(string2Number(value)); setAge(string2Number(value));
}, []); }, []);
const handleInputHeight = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
if (Number.isNaN(Number(value))) {
return;
}
setHeight(string2Number(value));
}, []);
const handleInputWeight = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
if (Number.isNaN(Number(value))) {
return;
}
setWeight(string2Number(value));
}, []);
const handleInputShoeSize = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
if (Number.isNaN(Number(value))) {
return;
}
setShoeSize(string2Number(value));
}, []);
const handleGenderChange = useCallback((value: GenderType) => { const handleGenderChange = useCallback((value: GenderType) => {
log('handleGenderChange', value); log('handleGenderChange', value);
setGender(value); setGender(value);
}, []); }, []);
const handleInputWorkedSecCategoryStr = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
setWorkedSecCategoryStr(value);
}, []);
useImperativeHandle( useImperativeHandle(
ref, ref,
() => ({ () => ({
getData: () => ({ name, gender, age, workedSecCategoryStr }), getData: () => ({ name, gender, age, height, weight, shoeSize }),
}), }),
[name, gender, age, workedSecCategoryStr] [name, gender, age, height, weight, shoeSize]
); );
return ( return (
@ -60,17 +82,29 @@ function ProfileBasicFragment(props: IProps, ref) {
<BlFormItem title="称呼"> <BlFormItem title="称呼">
<BlFormInput value={name} onInput={handleInputName} /> <BlFormInput value={name} onInput={handleInputName} />
</BlFormItem> </BlFormItem>
<BlFormItem title="年龄">
<BlFormInput value={String(age)} type="number" maxlength={3} onInput={handleInputAge} rightText="岁" />
</BlFormItem>
<BlFormItem title="性别"> <BlFormItem title="性别">
<BlFormRadioGroup direction="horizontal" value={gender} onChange={handleGenderChange}> <BlFormRadioGroup direction="horizontal" value={gender} onChange={handleGenderChange}>
<BlFormRadio name={GenderType.WOMEN} text="女" value={gender} /> <BlFormRadio name={GenderType.WOMEN} text="女" value={gender} />
<BlFormRadio name={GenderType.MEN} text="男" value={gender} /> <BlFormRadio name={GenderType.MEN} text="男" value={gender} />
</BlFormRadioGroup> </BlFormRadioGroup>
</BlFormItem> </BlFormItem>
<BlFormItem title="直播过的品类" optional> <BlFormItem title="年龄">
<BlFormInput value={workedSecCategoryStr} maxlength={140} onInput={handleInputWorkedSecCategoryStr} /> <BlFormInput value={String(age)} type="number" maxlength={3} onInput={handleInputAge} rightText="岁" />
</BlFormItem>
<BlFormItem title="身高">
<BlFormInput value={String(height)} type="number" maxlength={3} onInput={handleInputHeight} rightText="CM" />
</BlFormItem>
<BlFormItem title="体重">
<BlFormInput value={String(weight)} type="number" maxlength={3} onInput={handleInputWeight} rightText="KG" />
</BlFormItem>
<BlFormItem title="鞋码" optional>
<BlFormInput
value={String(shoeSize)}
type="number"
maxlength={2}
onInput={handleInputShoeSize}
rightText="码"
/>
</BlFormItem> </BlFormItem>
</div> </div>
); );

View File

@ -48,9 +48,10 @@ function ProfileExperienceFragment(props: IProps, ref) {
const [workedYear, setWorkedYear] = useState(profile.workedYear); const [workedYear, setWorkedYear] = useState(profile.workedYear);
const [workedAccounts, setWorkedAccounts] = useState(profile.workedAccounts || ''); const [workedAccounts, setWorkedAccounts] = useState(profile.workedAccounts || '');
const [newAccountExperience, setNewAccountExperience] = useState(profile.newAccountExperience); const [newAccountExperience, setNewAccountExperience] = useState(profile.newAccountExperience);
const [workedSecCategoryStr, setWorkedSecCategoryStr] = useState(profile.workedSecCategoryStr || '');
// const [style, setStyle] = useState(calcInitStyle(profile.style)); // const [style, setStyle] = useState(calcInitStyle(profile.style));
const [maxGmv, setMaxGmv] = useState(calcInitMaxGmv(profile?.maxGmv || 0)); const [maxGmv, setMaxGmv] = useState(calcInitMaxGmv(profile?.maxGmv || 0));
// const [maxOnline, setMaxOnline] = useState(profile.maxOnline || ''); const [maxOnline, setMaxOnline] = useState(profile.maxOnline || '');
const handleSelectWorkYear = useCallback((newYear: WorkedYears) => { const handleSelectWorkYear = useCallback((newYear: WorkedYears) => {
setWorkedYear(newYear); setWorkedYear(newYear);
@ -66,6 +67,11 @@ function ProfileExperienceFragment(props: IProps, ref) {
setNewAccountExperience(value); setNewAccountExperience(value);
}, []); }, []);
const handleInputWorkedSecCategoryStr = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
setWorkedSecCategoryStr(value);
}, []);
// const handleStyleChange = useCallback((value: StyleType[]) => { // const handleStyleChange = useCallback((value: StyleType[]) => {
// log('handleStyleChange', value); // log('handleStyleChange', value);
// setStyle(value); // setStyle(value);
@ -78,14 +84,14 @@ function ProfileExperienceFragment(props: IProps, ref) {
} }
setMaxGmv(string2Number(value)); setMaxGmv(string2Number(value));
}, []); }, []);
//
// const handleInputMaxOnline = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => { const handleInputMaxOnline = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
// const value = e.detail.value || ''; const value = e.detail.value || '';
// if (Number.isNaN(Number(value))) { if (Number.isNaN(Number(value))) {
// return; return;
// } }
// setMaxOnline(string2Number(value)); setMaxOnline(string2Number(value));
// }, []); }, []);
useImperativeHandle( useImperativeHandle(
ref, ref,
@ -94,21 +100,22 @@ function ProfileExperienceFragment(props: IProps, ref) {
workedYear, workedYear,
workedAccounts, workedAccounts,
newAccountExperience, newAccountExperience,
workedSecCategoryStr,
// style: getStyleString(style.sort()), // style: getStyleString(style.sort()),
// maxOnline: maxOnline === '' ? undefined : maxOnline, maxOnline: maxOnline === '' ? undefined : maxOnline,
maxGmv: maxGmv === '' ? undefined : getMaxGmv(Number(maxGmv)), maxGmv: maxGmv === '' ? undefined : getMaxGmv(Number(maxGmv)),
}), }),
}), }),
[workedYear, workedAccounts, newAccountExperience, maxGmv] [workedYear, workedAccounts, newAccountExperience, workedSecCategoryStr, maxGmv, maxOnline]
); );
return ( return (
<div className={PREFIX}> <div className={PREFIX}>
<BlFormItem title="直播经验" optional> <BlFormItem title="直播经验">
<BlFormSelect title="直播经验" value={workedYear} options={WORK_YEAR_OPTIONS} onSelect={handleSelectWorkYear} /> <BlFormSelect title="直播经验" value={workedYear} options={WORK_YEAR_OPTIONS} onSelect={handleSelectWorkYear} />
</BlFormItem> </BlFormItem>
<BlFormItem title="直播过的账号" optional> <BlFormItem title="直播过的品类" optional>
<BlFormInput value={workedAccounts} maxlength={140} onInput={handleInputWorkedAccounts} /> <BlFormInput value={workedSecCategoryStr} maxlength={140} onInput={handleInputWorkedSecCategoryStr} />
</BlFormItem> </BlFormItem>
<BlFormItem title="自然流起号经验" optional> <BlFormItem title="自然流起号经验" optional>
<BlFormRadioGroup <BlFormRadioGroup
@ -120,6 +127,9 @@ function ProfileExperienceFragment(props: IProps, ref) {
<BlFormRadio name text="有" value={newAccountExperience} /> <BlFormRadio name text="有" value={newAccountExperience} />
</BlFormRadioGroup> </BlFormRadioGroup>
</BlFormItem> </BlFormItem>
<BlFormItem title="直播过的账号" optional>
<BlFormInput value={workedAccounts} maxlength={140} onInput={handleInputWorkedAccounts} />
</BlFormItem>
{/* <ProfileEditItem title="直播风格" optional> {/* <ProfileEditItem title="直播风格" optional>
<ProfileCheckboxGroup value={style} onChange={handleStyleChange}> <ProfileCheckboxGroup value={style} onChange={handleStyleChange}>
<ProfileCheckbox value={style} name={StyleType.Broadcasting} text="平播" /> <ProfileCheckbox value={style} name={StyleType.Broadcasting} text="平播" />
@ -130,9 +140,9 @@ function ProfileExperienceFragment(props: IProps, ref) {
<BlFormItem title="最高GMV" optional> <BlFormItem title="最高GMV" optional>
<BlFormInput value={String(maxGmv)} type="number" maxlength={10} onInput={handleInputMaxGmv} rightText="W" /> <BlFormInput value={String(maxGmv)} type="number" maxlength={10} onInput={handleInputMaxGmv} rightText="W" />
</BlFormItem> </BlFormItem>
{/*<BlFormItem title="最高在线人数" optional>*/} <BlFormItem title="最高在线人数" optional>
{/* <BlFormInput value={String(maxOnline)} type="number" onInput={handleInputMaxOnline} rightText="人" />*/} <BlFormInput value={String(maxOnline)} type="number" onInput={handleInputMaxOnline} rightText="人" />
{/*</BlFormItem>*/} </BlFormItem>
</div> </div>
); );
} }

View File

@ -45,8 +45,4 @@
} }
} }
.transparent {
background: transparent;
padding: 0;
}
} }

View File

@ -3,21 +3,12 @@ import Taro from '@tarojs/taro';
import { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from 'react'; import { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from 'react';
import { BlCheckbox, BlCheckboxGroup } from '@/components/bl-checkbox';
import BlFormItem from '@/components/bl-form-item'; import BlFormItem from '@/components/bl-form-item';
import { BlFormRadio, BlFormRadioGroup } from '@/components/bl-form-radio'; import { BlFormRadio, BlFormRadioGroup } from '@/components/bl-form-radio';
import BlFormSelect from '@/components/bl-form-select'; import BlFormSelect from '@/components/bl-form-select';
import { EventName, OpenSource, PageUrl } from '@/constants/app'; import { EventName, OpenSource, PageUrl } from '@/constants/app';
import { CITY_CODE_TO_NAME_MAP } from '@/constants/city'; import { CITY_CODE_TO_NAME_MAP } from '@/constants/city';
import { import { EmployType, EMPLOY_TYPE_TITLE_MAP, FULL_PRICE_OPTIONS, PART_PRICE_OPTIONS } from '@/constants/job';
EmployType,
EMPLOY_TYPE_TITLE_MAP,
FULL_PRICE_OPTIONS,
PART_PRICE_OPTIONS,
ALL_JOB_TYPES,
JobType,
JOB_TYPE_TITLE_MAP,
} from '@/constants/job';
import { SalaryRange } from '@/types/job'; import { SalaryRange } from '@/types/job';
import { MaterialProfile } from '@/types/material'; import { MaterialProfile } from '@/types/material';
import { logWithPrefix } from '@/utils/common'; import { logWithPrefix } from '@/utils/common';
@ -36,7 +27,7 @@ const log = logWithPrefix(PREFIX);
const realtimeLogger = Taro.getRealtimeLogManager(); const realtimeLogger = Taro.getRealtimeLogManager();
realtimeLogger.tag(PREFIX); realtimeLogger.tag(PREFIX);
const calcInitCityCodes = (codeString: string = '') => { const calcInitCityCodes = (codeString: string = '') => {
const codes = (codeString || '').split('、'); const codes = codeString.split('、');
return codes.filter(code => !!CITY_CODE_TO_NAME_MAP.get(code)); return codes.filter(code => !!CITY_CODE_TO_NAME_MAP.get(code));
}; };
@ -69,20 +60,13 @@ const getSalaryPrice = (fullSalary?: SalaryRange, partSalary?: SalaryRange) => {
return price; return price;
}; };
const getWorkedCategoryString = (workedCategory: JobType[]) => workedCategory.join(',');
const getWorkedCategory = (workedCategory?: string): JobType[] => {
return workedCategory ? (workedCategory.split(',') as JobType[]).filter(v => ALL_JOB_TYPES.includes(v)) : [];
};
function ProfileIntentionFragment(props: IProps, ref) { function ProfileIntentionFragment(props: IProps, ref) {
const { profile } = props; const { profile } = props;
const [cityCodes, setCityCodes] = useState(calcInitCityCodes(profile.cityCodes || '')); const [cityCodes, setCityCodes] = useState(calcInitCityCodes(profile.cityCodes));
const [employType, setEmployType] = useState(profile.employType); const [employType, setEmployType] = useState(profile.employType);
const [fullSalary, setFullSalary] = useState(calcSalarySelect(profile, true)); const [fullSalary, setFullSalary] = useState(calcSalarySelect(profile, true));
const [partSalary, setPartSalary] = useState(calcSalarySelect(profile, false)); const [partSalary, setPartSalary] = useState(calcSalarySelect(profile, false));
const [acceptWorkForSit, setAcceptWorkForSit] = useState(profile.acceptWorkForSit); const [acceptWorkForSit, setAcceptWorkForSit] = useState(profile.acceptWorkForSit);
const [workedCategory, setWorkedCategory] = useState<JobType[]>(getWorkedCategory(profile.workedCategory));
const handleDeleteCity = useCallback( const handleDeleteCity = useCallback(
(deleteCode: string) => { (deleteCode: string) => {
@ -101,7 +85,7 @@ function ProfileIntentionFragment(props: IProps, ref) {
data => { data => {
log('handleSelectCity', data); log('handleSelectCity', data);
realtimeLogger.info('handleSelectCity', data); realtimeLogger.info('handleSelectCity', data);
const { cityCode: code } = data; const { openSource, cityCode: code } = data;
// if (openSource !== OpenSource.AddIndentCity) { // if (openSource !== OpenSource.AddIndentCity) {
// return; // return;
// } // }
@ -134,11 +118,6 @@ function ProfileIntentionFragment(props: IProps, ref) {
setAcceptWorkForSit(value); setAcceptWorkForSit(value);
}, []); }, []);
const handleWorkedCategoryChange = useCallback((value: JobType[]) => {
log('handleWorkedCategoryChange', value);
setWorkedCategory(value);
}, []);
useEffect(() => { useEffect(() => {
Taro.eventCenter.on(EventName.SELECT_CITY, handleSelectCity); Taro.eventCenter.on(EventName.SELECT_CITY, handleSelectCity);
return () => { return () => {
@ -153,11 +132,10 @@ function ProfileIntentionFragment(props: IProps, ref) {
cityCodes: getCityCodeString(cityCodes), cityCodes: getCityCodeString(cityCodes),
employType, employType,
acceptWorkForSit, acceptWorkForSit,
workedCategory: getWorkedCategoryString(workedCategory),
...getSalaryPrice(fullSalary, partSalary), ...getSalaryPrice(fullSalary, partSalary),
}), }),
}), }),
[cityCodes, employType, fullSalary, partSalary, acceptWorkForSit, workedCategory] [cityCodes, employType, fullSalary, partSalary, acceptWorkForSit]
); );
return ( return (
@ -196,7 +174,7 @@ function ProfileIntentionFragment(props: IProps, ref) {
</BlFormRadioGroup> </BlFormRadioGroup>
</BlFormItem> </BlFormItem>
{isFullTimePriceRequired(employType) && ( {isFullTimePriceRequired(employType) && (
<BlFormItem title="期望全职薪资" subTitle="重要"> <BlFormItem title="期望全职薪资">
<BlFormSelect <BlFormSelect
title="期望全职薪资" title="期望全职薪资"
value={fullSalary} value={fullSalary}
@ -206,7 +184,7 @@ function ProfileIntentionFragment(props: IProps, ref) {
</BlFormItem> </BlFormItem>
)} )}
{isPartTimePriceRequired(employType) && ( {isPartTimePriceRequired(employType) && (
<BlFormItem title="期望兼职薪资" subTitle="重要"> <BlFormItem title="期望兼职薪资">
<BlFormSelect <BlFormSelect
title="期望兼职薪资" title="期望兼职薪资"
value={partSalary} value={partSalary}
@ -215,19 +193,12 @@ function ProfileIntentionFragment(props: IProps, ref) {
/> />
</BlFormItem> </BlFormItem>
)} )}
<BlFormItem title="是否接受坐班" optional> <BlFormItem title="是否接受坐班">
<BlFormRadioGroup direction="horizontal" value={acceptWorkForSit} onChange={handleAcceptSittingChange}> <BlFormRadioGroup direction="horizontal" value={acceptWorkForSit} onChange={handleAcceptSittingChange}>
<BlFormRadio name text="接受" value={acceptWorkForSit} /> <BlFormRadio name text="接受" value={acceptWorkForSit} />
<BlFormRadio name={false} text="不接受" value={acceptWorkForSit} /> <BlFormRadio name={false} text="不接受" value={acceptWorkForSit} />
</BlFormRadioGroup> </BlFormRadioGroup>
</BlFormItem> </BlFormItem>
<BlFormItem title="意向类目" subTitle="多选" dynamicHeight contentClassName="transparent">
<BlCheckboxGroup value={workedCategory} onChange={handleWorkedCategoryChange}>
{ALL_JOB_TYPES.filter(type => type !== JobType.All).map(type => {
return <BlCheckbox text={JOB_TYPE_TITLE_MAP[type]} key={type} name={type} />;
})}
</BlCheckboxGroup>
</BlFormItem>
</div> </div>
); );
} }

View File

@ -1,15 +0,0 @@
@import '@/styles/common.less';
@import '@/styles/variables.less';
.fragment-profile-others {
width: 100%;
&__title {
font-weight: 400;
font-size: 32px;
line-height: 40px;
margin-bottom: 24px;
}
}

View File

@ -1,94 +0,0 @@
import { BaseEventOrig, InputProps } from '@tarojs/components';
import { forwardRef, useCallback, useImperativeHandle, useState } from 'react';
import BlFormInput from '@/components/bl-form-input';
import BlFormItem from '@/components/bl-form-item';
import BlFormSelect from '@/components/bl-form-select';
import { EDUCATION_TYPE_OPTIONS, EducationType } from '@/constants/material';
import { MaterialProfile } from '@/types/material';
import { logWithPrefix, string2Number } from '@/utils/common';
import './index.less';
interface IProps {
profile: Partial<MaterialProfile>;
}
const PREFIX = 'fragment-profile-others';
const log = logWithPrefix(PREFIX);
function ProfileOthersFragment(props: IProps, ref) {
const { profile } = props;
const [educationType, setEducationType] = useState(profile.educationType || '');
const [height, setHeight] = useState(profile.height || '');
const [weight, setWeight] = useState(profile.weight || '');
const [shoeSize, setShoeSize] = useState(profile.shoeSize || '');
const handleInputHeight = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
if (Number.isNaN(Number(value))) {
return;
}
setHeight(string2Number(value));
}, []);
const handleInputWeight = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
if (Number.isNaN(Number(value))) {
return;
}
setWeight(string2Number(value));
}, []);
const handleInputShoeSize = useCallback((e: BaseEventOrig<InputProps.inputEventDetail>) => {
const value = e.detail.value || '';
if (Number.isNaN(Number(value))) {
return;
}
setShoeSize(string2Number(value));
}, []);
const handleSelectEducationType = useCallback((type: EducationType) => {
setEducationType(type);
}, []);
useImperativeHandle(
ref,
() => ({
getData: () => ({ height, weight, shoeSize, educationType }),
}),
[height, weight, shoeSize, educationType]
);
return (
<div className={PREFIX}>
<div className={`${PREFIX}__title`}>穿</div>
<BlFormItem title="身高" optional>
<BlFormInput value={String(height)} type="number" maxlength={3} onInput={handleInputHeight} rightText="CM" />
</BlFormItem>
<BlFormItem title="体重" optional>
<BlFormInput value={String(weight)} type="number" maxlength={3} onInput={handleInputWeight} rightText="KG" />
</BlFormItem>
<BlFormItem title="鞋码" optional>
<BlFormInput
value={String(shoeSize)}
type="number"
maxlength={2}
onInput={handleInputShoeSize}
rightText="码"
/>
</BlFormItem>
<BlFormItem title="学历" optional>
<BlFormSelect
title="学历"
value={educationType}
options={EDUCATION_TYPE_OPTIONS}
onSelect={handleSelectEducationType}
/>
</BlFormItem>
</div>
);
}
export default forwardRef(ProfileOthersFragment);

View File

@ -44,7 +44,7 @@
font-size: 28px; font-size: 28px;
font-weight: 400; font-weight: 400;
color: #ffffff; color: #ffffff;
background: #6D3DF5B2; background: #0000005c;
border-radius: 48px; border-radius: 48px;
} }
@ -232,8 +232,4 @@
} }
} }
} }
.join-group-hint {
margin: 24px;
}
} }

View File

@ -8,21 +8,14 @@ import DevDiv from '@/components/dev-div';
import SafeBottomPadding from '@/components/safe-bottom-padding'; import SafeBottomPadding from '@/components/safe-bottom-padding';
import { OpenSource, PageUrl } from '@/constants/app'; import { OpenSource, PageUrl } from '@/constants/app';
import { CITY_CODE_TO_NAME_MAP } from '@/constants/city'; import { CITY_CODE_TO_NAME_MAP } from '@/constants/city';
import { FULL_PRICE_OPTIONS, JOB_TYPE_TITLE_MAP, JobType, PART_PRICE_OPTIONS } from '@/constants/job'; import { FULL_PRICE_OPTIONS, PART_PRICE_OPTIONS } from '@/constants/job';
import { import { ProfileGroupType, ProfileTitleMap, WORK_YEAR_OPTIONS } from '@/constants/material';
EDUCATION_TYPE_LABELS,
GenderType,
ProfileGroupType,
ProfileTitleMap,
WORK_YEAR_OPTIONS,
} from '@/constants/material';
import { MaterialProfile } from '@/types/material'; import { MaterialProfile } from '@/types/material';
import { isDesktop, logWithPrefix } from '@/utils/common'; import { logWithPrefix, isDesktop } from '@/utils/common';
import { sortVideos } from '@/utils/material'; import { getBasicInfo, sortVideos } from '@/utils/material';
import { navigateTo } from '@/utils/route'; import { navigateTo } from '@/utils/route';
import './index.less'; import './index.less';
import { JoinGroupHint } from '@/components/join-group-hint';
import onChangeEventDetail = SwiperProps.onChangeEventDetail; import onChangeEventDetail = SwiperProps.onChangeEventDetail;
interface IProps { interface IProps {
@ -36,7 +29,7 @@ const log = logWithPrefix(PREFIX);
const DEFAULT_TEXT = '未填写'; const DEFAULT_TEXT = '未填写';
const getIndentCity = (codeString: string = '') => { const getIndentCity = (codeString: string = '') => {
const codes = (codeString || '').split('、'); const codes = codeString.split('、');
const cityNames: string[] = []; const cityNames: string[] = [];
codes.forEach(code => { codes.forEach(code => {
const cityName = CITY_CODE_TO_NAME_MAP.get(code); const cityName = CITY_CODE_TO_NAME_MAP.get(code);
@ -69,14 +62,7 @@ const getWorkYear = (year: number) => {
const y = Math.min(year, 3); const y = Math.min(year, 3);
return WORK_YEAR_OPTIONS.find(option => option.value === y)?.label || `${y}`; return WORK_YEAR_OPTIONS.find(option => option.value === y)?.label || `${y}`;
}; };
const getWorkedCategory = (workedCategory?: string): string => {
return workedCategory
? (workedCategory.split(',') as JobType[])
.map(v => (v !== JobType.All ? JOB_TYPE_TITLE_MAP[v] : null))
.filter(Boolean)
.join('、')
: '';
};
const getDataGroup = (profile: MaterialProfile | null) => { const getDataGroup = (profile: MaterialProfile | null) => {
if (!profile) { if (!profile) {
return []; return [];
@ -90,7 +76,6 @@ const getDataGroup = (profile: MaterialProfile | null) => {
{ title: '兼职薪资', value: getIndentPrice(profile.partyTimeMinPrice, profile.partyTimeMaxPrice, false) || '' }, { title: '兼职薪资', value: getIndentPrice(profile.partyTimeMinPrice, profile.partyTimeMaxPrice, false) || '' },
{ title: '意向城市', value: getIndentCity(profile.cityCodes) || DEFAULT_TEXT }, { title: '意向城市', value: getIndentCity(profile.cityCodes) || DEFAULT_TEXT },
{ title: '是否接受坐班', value: profile.acceptWorkForSit ? '是' : '否' }, { title: '是否接受坐班', value: profile.acceptWorkForSit ? '是' : '否' },
{ title: '意向品类', value: getWorkedCategory(profile.workedCategory) || DEFAULT_TEXT, fullLine: true },
], ],
}, },
{ {
@ -100,18 +85,9 @@ const getDataGroup = (profile: MaterialProfile | null) => {
{ title: '直播年限', value: getWorkYear(profile.workedYear) || DEFAULT_TEXT }, { title: '直播年限', value: getWorkYear(profile.workedYear) || DEFAULT_TEXT },
{ title: '直播过的账号', value: profile.workedAccounts || DEFAULT_TEXT }, { title: '直播过的账号', value: profile.workedAccounts || DEFAULT_TEXT },
{ title: '自然流起号经验', value: Boolean(profile.newAccountExperience) ? '有' : '无' }, { title: '自然流起号经验', value: Boolean(profile.newAccountExperience) ? '有' : '无' },
// { title: '直播过的品类', value: profile.workedSecCategoryStr || DEFAULT_TEXT }, { title: '直播过的品类', value: profile.workedSecCategoryStr || DEFAULT_TEXT },
{ title: '最高GMV', value: profile.maxGmv ? `${profile.maxGmv / 10000}` : DEFAULT_TEXT }, { title: '最高GMV', value: profile.maxGmv ? `${profile.maxGmv / 10000}` : DEFAULT_TEXT },
], { title: '最高在线人数', value: profile.maxOnline || DEFAULT_TEXT },
},
{
title: ProfileTitleMap[ProfileGroupType.Others],
type: ProfileGroupType.Others,
items: [
{ title: '身高', value: profile.height ? `${profile.height}cm` : DEFAULT_TEXT },
{ title: '体重', value: profile.weight ? `${profile.weight}kg` : DEFAULT_TEXT },
{ title: '鞋码', value: profile.shoeSize ? `${profile.shoeSize}` : DEFAULT_TEXT },
{ title: '学历', value: EDUCATION_TYPE_LABELS[profile.educationType] || DEFAULT_TEXT },
], ],
}, },
{ {
@ -122,16 +98,6 @@ const getDataGroup = (profile: MaterialProfile | null) => {
]; ];
}; };
export const getBasicInfo = (profile: Pick<MaterialProfile, 'age' | 'gender'>) => {
const result: string[] = [];
if (typeof profile.age !== 'undefined' && profile.age !== null) {
result.push(`${profile.age}`);
}
result.push(profile.gender === GenderType.MEN ? '男' : '女');
return result.join('·');
};
export default function ProfileViewFragment(props: IProps) { export default function ProfileViewFragment(props: IProps) {
const { profile, editable, onDev } = props; const { profile, editable, onDev } = props;
const [coverIndex, setCoverIndex] = useState(0); const [coverIndex, setCoverIndex] = useState(0);
@ -241,7 +207,6 @@ export default function ProfileViewFragment(props: IProps) {
>{`${coverIndex + 1}/${profile.materialVideoInfoList.length}`}</div> >{`${coverIndex + 1}/${profile.materialVideoInfoList.length}`}</div>
</div> </div>
</div> </div>
{!editable && <JoinGroupHint />}
<div className={`${PREFIX}__body`}> <div className={`${PREFIX}__body`}>
<div className={`${PREFIX}__basic-info`}> <div className={`${PREFIX}__basic-info`}>
<div className={`${PREFIX}__basic-info__name-container`}> <div className={`${PREFIX}__basic-info__name-container`}>
@ -254,10 +219,7 @@ export default function ProfileViewFragment(props: IProps) {
</div> </div>
)} )}
</div> </div>
<DevDiv className={`${PREFIX}__basic-info__content`} OnDev={onDev}> <div className={`${PREFIX}__basic-info__content`}>{getBasicInfo(profile)}</div>
<div>{getBasicInfo(profile)}</div>
<div>{profile.workedSecCategoryStr ? `播过:${profile.workedSecCategoryStr}` : ''}</div>
</DevDiv>
<div className={`${PREFIX}__divider`} /> <div className={`${PREFIX}__divider`} />
</div> </div>
{dataGroup.map((data, index: number) => ( {dataGroup.map((data, index: number) => (
@ -266,9 +228,7 @@ export default function ProfileViewFragment(props: IProps) {
{index ? ( {index ? (
<div className={`${PREFIX}__info-group__header__title`}>{data.title}</div> <div className={`${PREFIX}__info-group__header__title`}>{data.title}</div>
) : ( ) : (
<DevDiv className={`${PREFIX}__info-group__header__title`} OnDev={onDev}> <DevDiv className={`${PREFIX}__info-group__header__title`} OnDev={onDev}>{data.title}</DevDiv>
{data.title}
</DevDiv>
)} )}
{editable && ( {editable && (
<div className={`${PREFIX}__info-group__header__edit`} onClick={() => handleEditGroupItem(data.type)}> <div className={`${PREFIX}__info-group__header__edit`} onClick={() => handleEditGroupItem(data.type)}>

View File

@ -17,25 +17,4 @@
border-radius: 16px; border-radius: 16px;
margin-top: 24px; margin-top: 24px;
} }
&__recommend {
display: inline-flex;
line-height: 36px;
padding: 0 8px;
height: 36px;
background: rgb(255, 80, 81);
border-radius: 4px;
font-size: 24px;
color: #fff;
align-items: center;
gap: 6px;
position: absolute;
left: 208px;
top: 8px;
width: 128px;
&-cell {
position: relative;
}
}
} }

View File

@ -1,7 +1,6 @@
import { Image } from '@tarojs/components'; import { Image } from '@tarojs/components';
import { Cell } from '@taroify/core'; import { Cell } from '@taroify/core';
import { GoodJob } from '@taroify/icons';
import { useCallback } from 'react'; import { useCallback } from 'react';
import MaterialCard from '@/components/material-card'; import MaterialCard from '@/components/material-card';
@ -19,8 +18,6 @@ export default function AnchorFragment() {
const handleClickSwitch = useCallback(() => switchRoleType(RoleType.Company), []); const handleClickSwitch = useCallback(() => switchRoleType(RoleType.Company), []);
const handleClickInviteOperations = useCallback(() => navigateTo(PageUrl.InviteOperations), []);
return ( return (
<div className={PREFIX}> <div className={PREFIX}>
<MaterialCard /> <MaterialCard />
@ -32,21 +29,6 @@ export default function AnchorFragment() {
className={`${PREFIX}__cell`} className={`${PREFIX}__cell`}
onClick={handleClickMyDeclaration} onClick={handleClickMyDeclaration}
/> />
<Cell
isLink
align="center"
title={
<div className={`${PREFIX}__recommend-cell`}>
<div className={`${PREFIX}__recommend`}>
<GoodJob />
</div>
</div>
}
className={`${PREFIX}__cell`}
onClick={handleClickInviteOperations}
/>
<Image <Image
mode="widthFix" mode="widthFix"
className={`${PREFIX}__switch-to-company`} className={`${PREFIX}__switch-to-company`}

View File

@ -13,21 +13,6 @@
.taroify-cell__value { .taroify-cell__value {
color: @blColor; color: @blColor;
} }
&-icon {
width: 40px;
height: 40px;
border-radius: 50%;
background: rgba(255, 80, 81, 0.16);
display: inline-flex;
justify-content: center;
align-items: center;
margin-right: 16px;
> image {
width: 18px;
height: 24px;
}
}
} }
&__go-publish-cell { &__go-publish-cell {
@ -39,25 +24,4 @@
color: @blHighlightColor; color: @blHighlightColor;
} }
} }
&__recommend {
display: inline-flex;
line-height: 36px;
padding: 0 8px;
height: 36px;
background: rgb(255, 80, 81);
border-radius: 4px;
font-size: 24px;
color: #fff;
align-items: center;
gap: 6px;
position: absolute;
left: 208px;
top: 8px;
width: 128px;
&-cell {
position: relative;
}
}
} }

View File

@ -1,13 +1,11 @@
import { Image } from '@tarojs/components';
import { Cell } from '@taroify/core'; import { Cell } from '@taroify/core';
import { GoodJob } from '@taroify/icons';
import classNames from 'classnames'; import classNames from 'classnames';
import { useCallback } from 'react'; import { useCallback } from 'react';
import CertificationStatus from '@/components/certification-status'; import CertificationStatus from '@/components/certification-status';
import WechatCell from '@/components/wx-cell'; import WechatCell from '@/components/wx-cell';
import { PageUrl } from '@/constants/app'; import { PageUrl } from '@/constants/app';
import { CertificationStatusType } from '@/constants/company';
import useUserInfo from '@/hooks/use-user-info'; import useUserInfo from '@/hooks/use-user-info';
import { navigateTo } from '@/utils/route'; import { navigateTo } from '@/utils/route';
@ -20,11 +18,13 @@ export default function CompanyFragment() {
// const [showPublish, setShowPublish] = useState(false); // const [showPublish, setShowPublish] = useState(false);
const handlePublishJob = useCallback(async () => { const handlePublishJob = useCallback(async () => {
if (userInfo.bossAuthStatus !== CertificationStatusType.Success) {
navigateTo(PageUrl.CertificationStart);
return;
}
navigateTo(PageUrl.CertificationManage); navigateTo(PageUrl.CertificationManage);
}, [userInfo]); }, [userInfo]);
const handleClickInviteOperations = useCallback(() => navigateTo(PageUrl.InviteOperations, { company: 1 }), []);
return ( return (
<div className={PREFIX}> <div className={PREFIX}>
<CertificationStatus className={`${PREFIX}__cell`} /> <CertificationStatus className={`${PREFIX}__cell`} />
@ -32,29 +32,7 @@ export default function CompanyFragment() {
<Cell <Cell
isLink isLink
align="center" align="center"
title={ title="发通告,让主播主动报单"
<div className={`${PREFIX}__recommend-cell`}>
<div className={`${PREFIX}__recommend`}>
<GoodJob />
</div>
</div>
}
className={`${PREFIX}__cell`}
onClick={handleClickInviteOperations}
/>
<Cell
isLink
align="center"
title={
<div>
<div className={`${PREFIX}__cell-icon`}>
<Image src="https://publiccdn.neighbourhood.com.cn/img/lightning.svg" />
</div>
</div>
}
className={classNames(`${PREFIX}__cell`, `${PREFIX}__go-publish-cell`)} className={classNames(`${PREFIX}__cell`, `${PREFIX}__go-publish-cell`)}
onClick={handlePublishJob} onClick={handlePublishJob}
> >

View File

@ -1,10 +0,0 @@
import { useSelector } from 'react-redux';
import { selectCreatedJob } from '@/store/selector/job';
function useCachedJobId() {
const data = useSelector(selectCreatedJob);
return data;
}
export default useCachedJobId;

View File

@ -1,10 +0,0 @@
import { useSelector } from 'react-redux';
import { selectCityConfigs } from '@/store/selector';
function useCityOperators() {
const data = useSelector(selectCityConfigs);
return data || [];
}
export default useCityOperators;

View File

@ -48,7 +48,7 @@ const CompanyTabs: TabItemType[] = [
{ {
type: PageType.BatchPublish, type: PageType.BatchPublish,
pagePath: PageUrl.UserBatchPublish, pagePath: PageUrl.UserBatchPublish,
text: '代发', text: '代招代发',
}, },
]; ];
@ -97,13 +97,6 @@ export const APP_CONFIG: AppConfigType = {
PageUrl.PrivacyWebview, PageUrl.PrivacyWebview,
PageUrl.Partner, PageUrl.Partner,
PageUrl.WithdrawRecord, PageUrl.WithdrawRecord,
PageUrl.GroupDelegatePublish,
PageUrl.GiveVip,
PageUrl.GroupOwnerCertificate,
PageUrl.PartnerShareVip,
PageUrl.InviteOperations,
PageUrl.AccelerateDelegatePublish,
PageUrl.GroupDetail,
// PageUrl.DevDebug, // PageUrl.DevDebug,
], ],
window: { window: {

View File

@ -1,13 +0,0 @@
import { useEffect, useRef } from 'react';
function usePrevious<T>(value: T) {
const ref = useRef<T>();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
export default usePrevious;

View File

@ -1,62 +0,0 @@
import Taro from '@tarojs/taro';
import { Dispatch, SetStateAction, useCallback, useState } from 'react';
import { EventName, PageUrl } from '@/constants/app';
import { CertificationStatusType } from '@/constants/company';
import { CollectEventName } from '@/constants/event';
import useUserInfo from '@/hooks/use-user-info';
import { RESPONSE_ERROR_CODE } from '@/http/constant';
import { HttpError } from '@/http/error';
import store from '@/store';
import { cacheJobId } from '@/store/actions';
import { collectEvent } from '@/utils/event';
import { postPublishJob } from '@/utils/job';
import { navigateTo } from '@/utils/route';
import Toast from '@/utils/toast';
export const usePublishJob = (jobId?: string): [boolean, Dispatch<SetStateAction<boolean>>, () => Promise<void>] => {
const userInfo = useUserInfo();
const [showBuy, setShowBuy] = useState(false);
const handlePublishJob = useCallback(async () => {
if (!jobId) {
return;
}
try {
Taro.showLoading();
await postPublishJob(jobId);
Taro.eventCenter.trigger(EventName.COMPANY_JOB_PUBLISH_CHANGED);
setShowBuy(false);
Toast.success('发布成功');
Taro.hideLoading();
if (userInfo.bossAuthStatus !== CertificationStatusType.Success) {
console.log('哈哈哈', jobId);
store.dispatch(cacheJobId(jobId));
navigateTo(PageUrl.CertificationStart);
}
} catch (error) {
Taro.hideLoading();
const e = error as HttpError;
const errorCode = e.errorCode;
const errorMsg = e.errorMsg || e.info?.() || e.message;
collectEvent(CollectEventName.PUBLISH_OPEN_JOB_FAILED, { jobId, error: errorMsg });
if (
errorCode === RESPONSE_ERROR_CODE.INSUFFICIENT_FREE_BALANCE ||
errorCode === RESPONSE_ERROR_CODE.BOSS_VIP_EXPIRED
) {
setShowBuy(true);
} else if (errorCode === RESPONSE_ERROR_CODE.INSUFFICIENT_BALANCE) {
Toast.info('您购买的产品已耗尽使用次数');
setShowBuy(true);
} else {
Toast.info(errorMsg || '发布失败请重试', 3000);
}
console.error(e);
}
}, [jobId, userInfo.bossAuthStatus]);
return [showBuy, setShowBuy, handlePublishJob];
};

View File

@ -3,8 +3,8 @@ import { useSelector } from 'react-redux';
import { selectUserInfo } from '@/store/selector/user'; import { selectUserInfo } from '@/store/selector/user';
function useUserInfo() { function useUserInfo() {
const userInfo = useSelector(selectUserInfo); const useInfo = useSelector(selectUserInfo);
return userInfo; return useInfo;
} }
export default useUserInfo; export default useUserInfo;

View File

@ -1,10 +1,9 @@
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { RoleType } from '@/constants/app';
import { selectRoleType } from '@/store/selector'; import { selectRoleType } from '@/store/selector';
function useRoleType() { function useRoleType() {
return useSelector(selectRoleType) || RoleType.Anchor; return useSelector(selectRoleType);
} }
export default useRoleType; export default useRoleType;

Some files were not shown because too many files have changed in this diff Show More