This commit is contained in:
gaofeng
2026-05-13 10:44:29 +08:00
commit 0b165153c6
3674 changed files with 316663 additions and 0 deletions

1
app/.htaccess Normal file
View File

@@ -0,0 +1 @@
deny from all

22
app/AppService.php Normal file
View File

@@ -0,0 +1,22 @@
<?php
declare (strict_types = 1);
namespace app;
use think\Service;
/**
* 应用服务类
*/
class AppService extends Service
{
public function register()
{
// 服务注册
}
public function boot()
{
// 服务启动
}
}

94
app/BaseController.php Normal file
View File

@@ -0,0 +1,94 @@
<?php
declare (strict_types = 1);
namespace app;
use think\App;
use think\exception\ValidateException;
use think\Validate;
/**
* 控制器基础类
*/
abstract class BaseController
{
/**
* Request实例
* @var \think\Request
*/
protected $request;
/**
* 应用实例
* @var \think\App
*/
protected $app;
/**
* 是否批量验证
* @var bool
*/
protected $batchValidate = false;
/**
* 控制器中间件
* @var array
*/
protected $middleware = [];
/**
* 构造方法
* @access public
* @param App $app 应用对象
*/
public function __construct(App $app)
{
$this->app = $app;
$this->request = $this->app->request;
// 控制器初始化
$this->initialize();
}
// 初始化
protected function initialize()
{}
/**
* 验证数据
* @access protected
* @param array $data 数据
* @param string|array $validate 验证器名或者验证规则数组
* @param array $message 提示信息
* @param bool $batch 是否批量验证
* @return array|string|true
* @throws ValidateException
*/
protected function validate(array $data, string|array $validate, array $message = [], bool $batch = false)
{
if (is_array($validate)) {
$v = new Validate();
$v->rule($validate);
} else {
if (strpos($validate, '.')) {
// 支持场景
[$validate, $scene] = explode('.', $validate);
}
$class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
$v = new $class();
if (!empty($scene)) {
$v->scene($scene);
}
}
$v->message($message);
// 是否批量验证
if ($batch || $this->batchValidate) {
$v->batch(true);
}
return $v->failException(true)->check($data);
}
}

58
app/ExceptionHandle.php Normal file
View File

@@ -0,0 +1,58 @@
<?php
namespace app;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\ValidateException;
use think\Response;
use Throwable;
/**
* 应用异常处理类
*/
class ExceptionHandle extends Handle
{
/**
* 不需要记录信息(日志)的异常类列表
* @var array
*/
protected $ignoreReport = [
HttpException::class,
HttpResponseException::class,
ModelNotFoundException::class,
DataNotFoundException::class,
ValidateException::class,
];
/**
* 记录异常信息(包括日志或者其它方式记录)
*
* @access public
* @param Throwable $exception
* @return void
*/
public function report(Throwable $exception): void
{
// 使用内置的方式记录异常日志
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @access public
* @param \think\Request $request
* @param Throwable $e
* @return Response
*/
public function render($request, Throwable $e): Response
{
// 添加自定义异常处理机制
// 其他错误交给系统处理
return parent::render($request, $e);
}
}

8
app/Request.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
namespace app;
// 应用请求对象类
class Request extends \think\Request
{
}

2
app/common.php Normal file
View File

@@ -0,0 +1,2 @@
<?php
// 应用公共文件

17
app/event.php Normal file
View File

@@ -0,0 +1,17 @@
<?php
// 事件定义文件
return [
'bind' => [
],
'listen' => [
'AppInit' => [],
'HttpRun' => [],
'HttpEnd' => [],
'LogLevel' => [],
'LogWrite' => [],
],
'subscribe' => [
],
];

859
app/home/common.php Normal file
View File

@@ -0,0 +1,859 @@
<?php
const COMPANY_TITLE='hzjw';
const MODEL = 'thailand';
const PAYMODEL = 'tyatjyzxthailand';
const PAY_MARK = 'tyatjyzxthailand';
const CONTACT_MODEL = 'contact';
const COUNTRY = '泰国';
const COUNTRY_EN = 'Thailand ';
const GET_WEB_CONFIG_URL = 'https://uniuser.jzvisa.com/home/Company/index/model/';
const BAIDUACCESSTOKENDIR = '/www/wwwroot/temcache/';
const BUCKET = 'hztha';
const SOURCE = 'evisa.hkpgsow.cn';
const OSS_URL = 'https://files.jzvisa.com/' . BUCKET . '/';
const PAY_TYPE = ['1' => 'wechatpay', '2' => 'alipay'];
const INCOICETYPE = '22';
const CONFIG_JSON_NATION = './json/nation.json';
const CONFIG_JSON_NATION_EN = './json/nation_en.json';
const CONFIG_JSON_COUNTRY = './json/country.json';
const CONFIG_JSON_COUNTRY_EN = './json/country_en.json';
const CONFIG_JSON_TH_CITY = './json/th_city.json';
const CONFIG_JSON_TH_DISTRICT = './json/th_district.json';
const CONFIG_JSON_TH_PROVINCE = './json/th_province.json';
const CONFIG_JSON_VISA_NATIONALITY = './json/visa_nationality.json';
const CONFIG_JSON_NATIONALITY_MAP = './json/visa_nationality_map.json';
const ARRIVE_DATE_COUNT = '3';
const BASE_PATH = '/tha';
const CONTACT_REASON = [
'我需要解决付款问题',
'我需要协助填写申请表格',
'我需要关于旅行证件的信息',
'我需要更正我的旅行证件或已提交申请的信息',
'我想检查已提交的申请的状态',
'我想申请退款',
'我有其他未列明的原因'
];
const CONTACT_REASON_EN = [
'I need to resolve a payment issue',
'I need assistance with filling out the application form',
'I need information about travel documents',
'I need to correct information on my travel document or submitted application',
'I want to check the status of my submitted application',
'I want to request a refund',
'I have other reasons not listed'
];
// 公共函数
function build_ocr_signature(string $appId, string $secret, ?int $timestamp = null): array
{
$timestamp = $timestamp ?? time();
$signString = $appId . "\n" . (string)$timestamp;
$signature = hash_hmac('sha256', $signString, $secret);
return [
'app_id' => $appId,
'timestamp' => (string)$timestamp,
'signature' => $signature,
'base_url' => config('app.OCR_BASE_URL'),
];
}
function isDateTodayTomorrowOrDayAfter($dateString, $country = '') {
// 将输入日期转换为 DateTime 对象
$inputDate = new DateTime($dateString);
$inputDate->setTime(0, 0, 0); // 忽略时间部分,只比较日期
// 获取今天的日期(午夜时间)
$today = new DateTime('today');
if ($inputDate < $today) {
return true;
}
// 获取明天和后天的日期
$tomorrow = new DateTime('tomorrow');
if ($country == 'thailand') {
$dayAfterTomorrow = new DateTime('tomorrow +1 day');
$dayAfterTomorrow1 = new DateTime('tomorrow +2 day');
return $inputDate == $today || $inputDate == $tomorrow || $inputDate == $dayAfterTomorrow || $inputDate == $dayAfterTomorrow1;
} else if ($country == 'indonesia') {
$yeasterday = new DateTime('yesterday');
$dayAfterTomorrow = new DateTime('tomorrow +1 day');
return $inputDate == $yeasterday || $inputDate == $today || $inputDate == $tomorrow || $inputDate == $dayAfterTomorrow;
} else {
$dayAfterTomorrow = new DateTime('tomorrow +1 day');
return $inputDate == $today || $inputDate == $tomorrow || $inputDate == $dayAfterTomorrow;
}
}
function find_json($json_name, $field, $field_value)
{
$data = file_get_contents($json_name);
$data_arr = json_decode($data, true);
$found = array_filter($data_arr, function ($item) use ($field, $field_value) {
return $item[$field] === $field_value;
});
// 输出结果
if ($found) {
$found = array_values($found); // 获取过滤后的第一个元素
return $found;
} else {
return false;
}
}
function getNameById($json_name, $id)
{
$data = file_get_contents($json_name);
$data_arr = json_decode($data, true);
foreach ($data_arr as $item) {
if (array_key_exists('data', $item['data'])) {
foreach ($item['data']['data'] as $dataItem) {
if ($dataItem['id'] === $id) {
return $dataItem['name'];
}
}
}
}
return null;
}
function getIdByName($json_name, $id)
{
$data = file_get_contents($json_name);
$data_arr = json_decode($data, true);
foreach ($data_arr as $item) {
if (array_key_exists('data', $item['data'])) {
foreach ($item['data']['data'] as $dataItem) {
if ($dataItem['name'] === $id) {
return $dataItem['id'];
}
}
}
}
return null;
}
function isMobile()
{
$_SERVER['ALL_HTTP'] = isset($_SERVER['ALL_HTTP']) ? $_SERVER['ALL_HTTP'] : '';
if (preg_match('/(up.browser|up.link|mmp|symbian|smartphone|midp|wap|phone|iphone|ipad|ipod|android|xoom)/i',
strtolower($_SERVER['HTTP_USER_AGENT']))) {
return true;
}
if ((isset($_SERVER['HTTP_ACCEPT'])) and (strpos(strtolower($_SERVER['HTTP_ACCEPT']),
'application/vnd.wap.xhtml+xml') !== false)) {
return true;
}
if (isset($_SERVER['HTTP_X_WAP_PROFILE'])) {
return true;
}
if (isset($_SERVER['HTTP_PROFILE'])) {
return true;
}
$mobile_ua = strtolower(substr($_SERVER['HTTP_USER_AGENT'], 0, 4));
$mobile_agents = array(
'w3c ',
'acs-',
'alav',
'alca',
'amoi',
'audi',
'avan',
'benq',
'bird',
'blac',
'blaz',
'brew',
'cell',
'cldc',
'cmd-',
'dang',
'doco',
'eric',
'hipt',
'inno',
'ipaq',
'java',
'jigs',
'kddi',
'keji',
'leno',
'lg-c',
'lg-d',
'lg-g',
'lge-',
'maui',
'maxo',
'midp',
'mits',
'mmef',
'mobi',
'mot-',
'moto',
'mwbp',
'nec-',
'newt',
'noki',
'oper',
'palm',
'pana',
'pant',
'phil',
'play',
'port',
'prox',
'qwap',
'sage',
'sams',
'sany',
'sch-',
'sec-',
'send',
'seri',
'sgh-',
'shar',
'sie-',
'siem',
'smal',
'smar',
'sony',
'sph-',
'symb',
't-mo',
'teli',
'tim-',
'tosh',
'tsm-',
'upg1',
'upsi',
'vk-v',
'voda',
'wap-',
'wapa',
'wapi',
'wapp',
'wapr',
'webc',
'winw',
'winw',
'xda',
'xda-'
);
if (in_array($mobile_ua, $mobile_agents)) {
return true;
}
if (strpos(strtolower($_SERVER['ALL_HTTP']), 'operamini') !== false) {
return true;
}
// Pre-final check to reset everything if the user is on Windows
if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows') !== false) {
return false;
}
// But WP7 is also Windows, with a slightly different characteristic
if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows phone') !== false) {
return true;
}
return false;
}
function getOrderNumber()
{
return '1003' . date('YmdHis') . mt_rand(10, 99);
}
function cmf_get_current_user_id()
{
$sessionUserId = session('user.id');
if (empty($sessionUserId)) {
return 0;
}
return $sessionUserId;
}
/**
* 身份证信息
* @param array $data
* @return void
*/
function parseIdentification(array $data)
{
$res = [
'national_identification_number' => '',
'provice' => '',
'city' => '',
'address' => ''
];
if (!is_array($data) || empty($data)) {
return $res;
}
$res['national_identification_number'] = $data['words_result']['公民身份号码']['words'] ?? '';
$address = paresAddress($data['words_result']['住址']['words'] ?? '');
$res['provice'] = $address['provice'];
$res['city'] = $address['city'];
$res['address'] = $address['address'];
return $res;
}
function getprovince($city)
{
if (empty($city)){
return '';
}
// 构建市与省份的对应关系数组
$cityProvinceMap = [
// 华北地区
"北京市" => "北京市",
"天津市" => "天津市",
"石家庄市" => "河北省",
"唐山市" => "河北省",
"秦皇岛市" => "河北省",
"邯郸市" => "河北省",
"邢台市" => "河北省",
"保定市" => "河北省",
"张家口市" => "河北省",
"承德市" => "河北省",
"沧州市" => "河北省",
"廊坊市" => "河北省",
"衡水市" => "河北省",
"太原市" => "山西省",
"大同市" => "山西省",
"阳泉市" => "山西省",
"长治市" => "山西省",
"晋城市" => "山西省",
"朔州市" => "山西省",
"晋中市" => "山西省",
"运城市" => "山西省",
"忻州市" => "山西省",
"临汾市" => "山西省",
"吕梁市" => "山西省",
"呼和浩特市" => "内蒙古自治区",
"包头市" => "内蒙古自治区",
"乌海市" => "内蒙古自治区",
"赤峰市" => "内蒙古自治区",
"通辽市" => "内蒙古自治区",
"鄂尔多斯市" => "内蒙古自治区",
"呼伦贝尔市" => "内蒙古自治区",
"巴彦淖尔市" => "内蒙古自治区",
"乌兰察布市" => "内蒙古自治区",
// 东北地区
"沈阳市" => "辽宁省",
"大连市" => "辽宁省",
"鞍山市" => "辽宁省",
"抚顺市" => "辽宁省",
"本溪市" => "辽宁省",
"丹东市" => "辽宁省",
"锦州市" => "辽宁省",
"营口市" => "辽宁省",
"阜新市" => "辽宁省",
"辽阳市" => "辽宁省",
"盘锦市" => "辽宁省",
"铁岭市" => "辽宁省",
"朝阳市" => "辽宁省",
"葫芦岛市" => "辽宁省",
"长春市" => "吉林省",
"吉林市" => "吉林省",
"四平市" => "吉林省",
"辽源市" => "吉林省",
"通化市" => "吉林省",
"白山市" => "吉林省",
"松原市" => "吉林省",
"白城市" => "吉林省",
"延边朝鲜族自治州" => "吉林省",
"哈尔滨市" => "黑龙江省",
"齐齐哈尔市" => "黑龙江省",
"鸡西市" => "黑龙江省",
"鹤岗市" => "黑龙江省",
"双鸭山市" => "黑龙江省",
"大庆市" => "黑龙江省",
"伊春市" => "黑龙江省",
"佳木斯市" => "黑龙江省",
"七台河市" => "黑龙江省",
"牡丹江市" => "黑龙江省",
"黑河市" => "黑龙江省",
"绥化市" => "黑龙江省",
"大兴安岭地区" => "黑龙江省",
// 华东地区
"上海市" => "上海市",
"南京市" => "江苏省",
"无锡市" => "江苏省",
"徐州市" => "江苏省",
"常州市" => "江苏省",
"苏州市" => "江苏省",
"南通市" => "江苏省",
"连云港市" => "江苏省",
"淮安市" => "江苏省",
"盐城市" => "江苏省",
"扬州市" => "江苏省",
"镇江市" => "江苏省",
"泰州市" => "江苏省",
"宿迁市" => "江苏省",
"杭州市" => "浙江省",
"宁波市" => "浙江省",
"温州市" => "浙江省",
"嘉兴市" => "浙江省",
"湖州市" => "浙江省",
"绍兴市" => "浙江省",
"金华市" => "浙江省",
"衢州市" => "浙江省",
"舟山市" => "浙江省",
"台州市" => "浙江省",
"丽水市" => "浙江省",
"合肥市" => "安徽省",
"芜湖市" => "安徽省",
"蚌埠市" => "安徽省",
"淮南市" => "安徽省",
"马鞍山市" => "安徽省",
"淮北市" => "安徽省",
"铜陵市" => "安徽省",
"安庆市" => "安徽省",
"黄山市" => "安徽省",
"滁州市" => "安徽省",
"阜阳市" => "安徽省",
"宿州市" => "安徽省",
"六安市" => "安徽省",
"亳州市" => "安徽省",
"池州市" => "安徽省",
"宣城市" => "安徽省",
"福州市" => "福建省",
"厦门市" => "福建省",
"莆田市" => "福建省",
"三明市" => "福建省",
"泉州市" => "福建省",
"漳州市" => "福建省",
"南平市" => "福建省",
"龙岩市" => "福建省",
"宁德市" => "福建省",
"南昌市" => "江西省",
"景德镇市" => "江西省",
"萍乡市" => "江西省",
"九江市" => "江西省",
"新余市" => "江西省",
"鹰潭市" => "江西省",
"赣州市" => "江西省",
"吉安市" => "江西省",
"宜春市" => "江西省",
"抚州市" => "江西省",
"上饶市" => "江西省",
"济南市" => "山东省",
"青岛市" => "山东省",
"淄博市" => "山东省",
"枣庄市" => "山东省",
"东营市" => "山东省",
"烟台市" => "山东省",
"潍坊市" => "山东省",
"济宁市" => "山东省",
"泰安市" => "山东省",
"威海市" => "山东省",
"日照市" => "山东省",
"临沂市" => "山东省",
"德州市" => "山东省",
"聊城市" => "山东省",
"滨州市" => "山东省",
"菏泽市" => "山东省",
// 中南地区
"郑州市" => "河南省",
"开封市" => "河南省",
"洛阳市" => "河南省",
"平顶山市" => "河南省",
"安阳市" => "河南省",
"鹤壁市" => "河南省",
"新乡市" => "河南省",
"焦作市" => "河南省",
"濮阳市" => "河南省",
"许昌市" => "河南省",
"漯河市" => "河南省",
"三门峡市" => "河南省",
"南阳市" => "河南省",
"商丘市" => "河南省",
"信阳市" => "河南省",
"周口市" => "河南省",
"驻马店市" => "河南省",
"武汉市" => "湖北省",
"黄石市" => "湖北省",
"十堰市" => "湖北省",
"宜昌市" => "湖北省",
"襄阳市" => "湖北省",
"鄂州市" => "湖北省",
"荆门市" => "湖北省",
"孝感市" => "湖北省",
"荆州市" => "湖北省",
"黄冈市" => "湖北省",
"咸宁市" => "湖北省",
"随州市" => "湖北省",
"恩施土家族苗族自治州" => "湖北省",
"长沙市" => "湖南省",
"株洲市" => "湖南省",
"湘潭市" => "湖南省",
"衡阳市" => "湖南省",
"邵阳市" => "湖南省",
"岳阳市" => "湖南省",
"常德市" => "湖南省",
"张家界市" => "湖南省",
"益阳市" => "湖南省",
"郴州市" => "湖南省",
"永州市" => "湖南省",
"怀化市" => "湖南省",
"娄底市" => "湖南省",
"湘西土家族苗族自治州" => "湖南省",
"广州市" => "广东省",
"韶关市" => "广东省",
"深圳市" => "广东省",
"珠海市" => "广东省",
"汕头市" => "广东省",
"佛山市" => "广东省",
"江门市" => "广东省",
"湛江市" => "广东省",
"茂名市" => "广东省",
"肇庆市" => "广东省",
"惠州市" => "广东省",
"梅州市" => "广东省",
"汕尾市" => "广东省",
"河源市" => "广东省",
"阳江市" => "广东省",
"清远市" => "广东省",
"东莞市" => "广东省",
"中山市" => "广东省",
"潮州市" => "广东省",
"揭阳市" => "广东省",
"云浮市" => "广东省",
"南宁市" => "广西壮族自治区",
"柳州市" => "广西壮族自治区",
"桂林市" => "广西壮族自治区",
"梧州市" => "广西壮族自治区",
"北海市" => "广西壮族自治区",
"防城港市" => "广西壮族自治区",
"钦州市" => "广西壮族自治区",
"贵港市" => "广西壮族自治区",
"玉林市" => "广西壮族自治区",
"百色市" => "广西壮族自治区",
"贺州市" => "广西壮族自治区",
"河池市" => "广西壮族自治区",
"来宾市" => "广西壮族自治区",
"崇左市" => "广西壮族自治区",
"海口市" => "海南省",
"三亚市" => "海南省",
"三沙市" => "海南省",
"儋州市" => "海南省",
// 西南地区
"重庆市" => "重庆市",
"成都市" => "四川省",
"自贡市" => "四川省",
"攀枝花市" => "四川省",
"泸州市" => "四川省",
"德阳市" => "四川省",
"绵阳市" => "四川省",
"广元市" => "四川省",
"遂宁市" => "四川省",
"内江市" => "四川省",
"乐山市" => "四川省",
"南充市" => "四川省",
"眉山市" => "四川省",
"宜宾市" => "四川省",
"广安市" => "四川省",
"达州市" => "四川省",
"雅安市" => "四川省",
"巴中市" => "四川省",
"资阳市" => "四川省",
"阿坝藏族羌族自治州" => "四川省",
"甘孜藏族自治州" => "四川省",
"凉山彝族自治州" => "四川省",
"贵阳市" => "贵州省",
"六盘水市" => "贵州省",
"遵义市" => "贵州省",
"安顺市" => "贵州省",
"毕节市" => "贵州省",
"铜仁市" => "贵州省",
"黔西南布依族苗族自治州" => "贵州省",
"黔东南苗族侗族自治州" => "贵州省",
"黔南布依族苗族自治州" => "贵州省",
"昆明市" => "云南省",
"曲靖市" => "云南省",
"玉溪市" => "云南省",
"保山市" => "云南省",
"昭通市" => "云南省",
"丽江市" => "云南省",
"普洱市" => "云南省",
"临沧市" => "云南省",
"楚雄彝族自治州" => "云南省",
"红河哈尼族彝族自治州" => "云南省",
"文山壮族苗族自治州" => "云南省",
"西双版纳傣族自治州" => "云南省",
"大理白族自治州" => "云南省",
"德宏傣族景颇族自治州" => "云南省",
"怒江傈僳族自治州" => "云南省",
"迪庆藏族自治州" => "云南省",
"拉萨市" => "西藏自治区",
"日喀则市" => "西藏自治区",
"昌都市" => "西藏自治区",
"林芝市" => "西藏自治区",
"山南市" => "西藏自治区",
"那曲市" => "西藏自治区",
"阿里地区" => "西藏自治区",
// 西北地区
"西安市" => "陕西省",
"铜川市" => "陕西省",
"宝鸡市" => "陕西省",
"咸阳市" => "陕西省",
"渭南市" => "陕西省",
"延安市" => "陕西省",
"汉中市" => "陕西省",
"榆林市" => "陕西省",
"安康市" => "陕西省",
"商洛市" => "陕西省",
"兰州市" => "甘肃省",
"嘉峪关市" => "甘肃省",
"金昌市" => "甘肃省",
"白银市" => "甘肃省",
"天水市" => "甘肃省",
"武威市" => "甘肃省",
"张掖市" => "甘肃省",
"平凉市" => "甘肃省",
"酒泉市" => "甘肃省",
"庆阳市" => "甘肃省",
"定西市" => "甘肃省",
"陇南市" => "甘肃省",
"临夏回族自治州" => "甘肃省",
"甘南藏族自治州" => "甘肃省",
"西宁市" => "青海省",
"海东市" => "青海省",
"海北藏族自治州" => "青海省",
"黄南藏族自治州" => "青海省",
"海南藏族自治州" => "青海省",
"果洛藏族自治州" => "青海省",
"玉树藏族自治州" => "青海省",
"海西蒙古族藏族自治州" => "青海省",
"银川市" => "宁夏回族自治区",
"石嘴山市" => "宁夏回族自治区",
"吴忠市" => "宁夏回族自治区",
"固原市" => "宁夏回族自治区",
"中卫市" => "宁夏回族自治区",
"乌鲁木齐市" => "新疆维吾尔自治区",
"克拉玛依市" => "新疆维吾尔自治区",
"吐鲁番市" => "新疆维吾尔自治区",
"哈密市" => "新疆维吾尔自治区",
"昌吉回族自治州" => "新疆维吾尔自治区",
"博尔塔拉蒙古自治州" => "新疆维吾尔自治区",
"巴音郭楞蒙古自治州" => "新疆维吾尔自治区",
"阿克苏地区" => "新疆维吾尔自治区",
"克孜勒苏柯尔克孜自治州" => "新疆维吾尔自治区",
"喀什地区" => "新疆维吾尔自治区",
"和田地区" => "新疆维吾尔自治区",
"伊犁哈萨克自治州" => "新疆维吾尔自治区",
"塔城地区" => "新疆维吾尔自治区",
"阿勒泰地区" => "新疆维吾尔自治区",
// 港澳台地区
"台北市" => "台湾省",
"新北市" => "台湾省",
"桃园市" => "台湾省",
"台中市" => "台湾省",
"台南市" => "台湾省",
"高雄市" => "台湾省",
"基隆市" => "台湾省",
"新竹市" => "台湾省",
"嘉义市" => "台湾省"
];
// 检查市名是否存在于数组中
if (array_key_exists($city, $cityProvinceMap)) {
return $cityProvinceMap[$city];
} else {
return '';
}
}
function paresAddress($address){
preg_match('/(.*?(省|自治区|北京|天津|上海|重庆))/', $address, $matches);
if (count($matches) > 1) {
$province = $matches[count($matches) - 2];
$address = preg_replace('/(.*?(省|自治区|北京|天津|上海|重庆))/','', $address, 1);
}
preg_match('/(.*?(市|自治州|地区|区划|县))/', $address, $matches);
if (count($matches) > 1) {
$city = $matches[count($matches) - 2];
$address = str_replace($city, '', $address);
}
preg_match('/(.*?(区|县|镇|乡|街道))/', $address, $matches);
if (count($matches) > 1) {
$area = $matches[count($matches) - 2];
$address = str_replace($area, '', $address);
}
$city = isset($city) ? $city : '';
if (empty($province)){
$province = getprovince($city);
}
$area = isset($area) ? $area : '';
return [
'provice' => isset($province) ? $province : '',
'city' => $city,
"address" => $area.$address
];
}
/**
* 护照信息
* @param array $data
* @return string[]
*/
function parsePassport(array $data)
{
$res = [
'last_name' => '',
'first_name' => '',
'last_zh_name' => '',
'first_zh_name' => '',
'city_birth' => '',
'birth_date_day' => '',
'birth_date_month' => '',
'birth_date_year' => '',
'birth_date' => '',
'gender' => '',
'passport_number' => '',
'passport_expedition_date_day' => '',
'passport_expedition_date_month' => '',
'passport_expedition_date_year' => '',
'passport_expedition_date' => '',
'passport_expiration_date_day' => '',
'passport_expiration_date_month' => '',
'passport_expiration_date_year' => '',
'passport_expiration_date' => '',
'country_code'=>'',
'place_issue' => '',
'nationality' =>''
];
if (!is_array($data) || empty($data)) {
return $res;
}
if (!empty($data['words_result']['姓名拼音']['words'])) {
$names = explode(',', $data['words_result']['姓名拼音']['words']);
$res['last_name'] = $names[0] ?? '';
$res['first_name'] = $names[1] ?? '';
}
if (!empty($data['words_result']['姓名']['words'])) {
$zhnames = parseChineseName($data['words_result']['姓名']['words']);
$res['last_zh_name'] = $zhnames['surname'] ?? '';
$res['first_zh_name'] = $zhnames['givenName'] ?? '';
}
if (!empty($data['words_result']['国家码']['words'])) {
$res['country_code'] = $data['words_result']['国家码']['words'];
}
if (!empty($data['words_result']['护照签发地点']['words'])) {
$citys = explode('/', $data['words_result']['护照签发地点']['words']);
$res['place_issue'] = $citys[1] ?? '';
}
if (!empty($data['words_result']['国籍']['words'])) {
$citys = explode('/', $data['words_result']['国籍']['words']);
$res['nationality'] = $citys[1] ?? '';
}
if (!empty($data['words_result']['出生地点']['words'])) {
$citys = explode('/', $data['words_result']['出生地点']['words']);
$res['city_birth'] = $citys[1] ?? '';
}
if (!empty($data['words_result']['护照号码']['words'])) {
$res['passport_number'] = $data['words_result']['护照号码']['words'];
}
if (!empty($data['words_result']['签发日期']['words'])) {
$res['passport_expedition_date_year'] = substr($data['words_result']['签发日期']['words'], 0, 4);
$res['passport_expedition_date_month'] = intval(substr($data['words_result']['签发日期']['words'], 4, 2));
$res['passport_expedition_date_day'] = intval(substr($data['words_result']['签发日期']['words'], -2));
$res['passport_expedition_date'] = $res['passport_expedition_date_year'] . '-' . $res['passport_expedition_date_month'] . '-' . $res['passport_expedition_date_day'];
}
if (!empty($data['words_result']['有效期至']['words'])) {
$res['passport_expiration_date_year'] = substr($data['words_result']['有效期至']['words'], 0, 4);
$res['passport_expiration_date_month'] = intval(substr($data['words_result']['有效期至']['words'], 4, 2));
$res['passport_expiration_date_day'] = intval(substr($data['words_result']['有效期至']['words'], -2));
$res['passport_expiration_date'] = $res['passport_expiration_date_year'] . '-' . $res['passport_expiration_date_month'] . '-' . $res['passport_expiration_date_day'];
}
if (!empty($data['words_result']['生日']['words'])) {
$res['birth_date_year'] = substr($data['words_result']['生日']['words'], 0, 4);
$res['birth_date_month'] = intval(substr($data['words_result']['生日']['words'], 4, 2));
$res['birth_date_day'] = intval(substr($data['words_result']['生日']['words'], -2));
$res['birth_date'] = $res['birth_date_year'] . '-' . $res['birth_date_month'] . '-' . $res['birth_date_day'];
}
if (!empty($data['words_result']['性别']['words'])) {
$names = explode('/', $data['words_result']['性别']['words']);
$res['gender'] = ($names[0] == '男') ? 'male' : 'female';
}
return $res;
}
/**
* 解析中文姓名为姓和名
*
* @param string $name 中文姓名
* @return array 包含姓和名的数组 ['surname' => '姓', 'givenName' => '名']
*/
function parseChineseName($name) {
// 移除姓名前后的空格
$name = trim($name);
// 如果姓名长度小于2无法合理分割返回原姓名作为姓氏
if (mb_strlen($name, 'UTF-8') < 2) {
return ['surname' => $name, 'givenName' => ''];
}
// 常见复姓列表(可根据需要扩展)
$compoundSurnames = [
'欧阳', '太史', '端木', '上官', '司马', '东方', '独孤', '南宫', '万俟',
'闻人', '夏侯', '诸葛', '尉迟', '公羊', '赫连', '澹台', '皇甫', '宗政',
'濮阳', '公冶', '太叔', '申屠', '公孙', '慕容', '仲孙', '钟离', '长孙',
'宇文', '司徒', '鲜于', '司空', '闾丘', '子车', '亓官', '司寇', '巫马',
'公西', '颛孙', '壤驷', '公良', '漆雕', '乐正', '宰父', '谷梁', '拓跋',
'夹谷', '轩辕', '令狐', '段干', '百里', '呼延', '东郭', '南门', '羊舌',
'微生', '公户', '公玉', '公仪', '梁丘', '公仲', '公上', '公门', '公山',
'公坚', '左丘', '公伯', '西门', '公祖', '第五', '公乘', '贯丘', '公皙',
'南荣', '东里', '东宫', '仲长', '子书', '子桑', '即墨', '达奚', '褚师'
];
// 检查是否是复姓
$firstTwoChars = mb_substr($name, 0, 2, 'UTF-8');
if (in_array($firstTwoChars, $compoundSurnames)) {
return [
'surname' => $firstTwoChars,
'givenName' => mb_substr($name, 2, null, 'UTF-8')
];
}
// 默认情况:第一个字符为姓,其余为名
return [
'surname' => mb_substr($name, 0, 1, 'UTF-8'),
'givenName' => mb_substr($name, 1, null, 'UTF-8')
];
}
/**
* 签证信息
* @param array $data
* @return string[]
*/
function parseVisa(array $data)
{
$res = [
'visa_foil_number' => '',
'passport_number' => '',
];
if (!is_array($data) || empty($data)) {
return $res;
}
foreach ($data['words_result'] as $arr) {
$str = preg_replace('/\s/i', '', $arr['words']);
if (preg_match('/^[G|E|P]([a-zA-Z0-9]{8})$/i', $str)) {
$res['passport_number'] = $str;
} elseif (preg_match('/^(P|H|J|K|L|M|N)\d{7}$/i', $str)) {
$res['visa_foil_number'] = $str;
}
}
return $res;
}
// 这是系统自动生成的公共文件

View File

@@ -0,0 +1,157 @@
<?php
declare (strict_types=1);
namespace app\home\controller;
use api\Httpcurl;
use app\BaseController;
use think\facade\Config;
use think\facade\Cookie;
use think\facade\Lang;
use think\facade\Log;
use think\facade\Request;
use think\facade\Session;
use think\facade\View;
/**
* 控制器基础类
*/
class Base extends BaseController
{
protected $_userId;
/**
* 构造方法
*/
public function initialize()
{
parent::initialize();
$source = Request::instance()->param('source', '');
if (!empty($source)) {
Session::set('source', $source);
} else {
$source = Session::get('source');
if (empty($source)) {
Session::set('source', $_SERVER['HTTP_HOST']);
} else {
Session::set('source', $source);
}
}
// 获取 当前用户ID
$this->_userId = cmf_get_current_user_id();
// 网站配置信息获取
if (!file_exists(app()->getRootPath().'data/config.json')) {
$domain = $_SERVER['HTTP_HOST'];
$web_config = Httpcurl::request(GET_WEB_CONFIG_URL . MODEL . '/domain/' . $domain, 'get');
if ($web_config[3]) {
abort(500, '网站配置信息获取失败');
}
$config_result = json_decode($web_config[0], true);
if (empty($config_result)) {
abort(500, '网站配置信息获取失败');
}
if ($config_result['code'] != 1) {
abort(500, '网站配置信息获取失败');
}
file_put_contents(app()->getRootPath().'data/config.json', json_encode($config_result));
} else {
$config_result = json_decode(file_get_contents(app()->getRootPath().'data/config.json'), true);
}
Lang::setLangSet(Cookie::get('think_var', 'zh-cn'));
View::assign('think_lang', Cookie::get('think_var'));
View::assign('menu_url', strtolower(Request::controller() . '/' . Request::action()));
/* URL 配置*/
Config::set(['GET_ORDER_DETAIL_URL' => $config_result['data']['get_order_detail_url']], 'app');
Config::set(['LOOKUP_URL' => $config_result['data']['lookup_url']], 'app');
Config::set(['CREATE_ORDER_URL' => $config_result['data']['create_order_url']], 'app');
Config::set(['NEWS_LIST' => $config_result['data']['news_list']], 'app');
Config::set(['NEWS_DETAIL' => $config_result['data']['news_detail']], 'app');
Config::set(['CREATE_CONTACT_URL' => $config_result['data']['create_contact_url']], 'app');
Config::set(['GET_ORDER_URL' => $config_result['data']['get_order_url']], 'app');
Config::set(['YZM_URL' => $config_result['data']['yzm_url']], 'app');
/*ocr 识别 配置参数 */
Config::set(['OCR_BASE_URL' => $config_result['data']['ocr_base_url']], 'app');
Config::set(['OCR_APP_ID' => $config_result['data']['ocr_app_id']], 'app');
Config::set(['OCR_SECRET' => $config_result['data']['ocr_secret']], 'app');
$ocr_config = build_ocr_signature($config_result['data']['ocr_app_id'], $config_result['data']['ocr_secret']);
View::assign('ocr_config', json_encode($ocr_config));
/* 国内支付配置*/
Config::set(['wechatpay' => $config_result['data']['pay']['wechatpay']], 'app');
Config::set(['wechath5pay' => $config_result['data']['pay']['wechath5pay']], 'app');
Config::set(['wechatpcpay' => $config_result['data']['pay']['wechatpcpay']], 'app');
Config::set(['alipcpay' => $config_result['data']['pay']['alipcpay']], 'app');
Config::set(['user_url' => $config_result['data']['user_url']], 'app');
Config::set(['order_url' => $config_result['data']['order_url']], 'app');
View::assign('company_address', $config_result['data']['company_address']);
View::assign('company_phone', $config_result['data']['phone'] ?? '');
View::assign('base_path', BASE_PATH);
View::assign('fapiao_kefu', $config_result['data']['fapiao_png']);
if (Cookie::get('think_var') == 'en-us') {
View::assign('footer', $config_result['data']['footer_en']);
View::assign('goods', $config_result['data']['TH_GOODS_EN']);
View::assign('country', COUNTRY_EN);
} else {
View::assign('country', COUNTRY);
View::assign('footer', $config_result['data']['footer']);
View::assign('goods', $config_result['data']['TH_GOODS']);
}
}
public function langs($lang='zh-cn')
{
Cookie::set('think_var', $lang);
return true;
}
public function success($msg = '操作成功', $url = '', $data = [], $wait = 3)
{
if (is_null($url) && isset($_SERVER["HTTP_REFERER"])) {
$url = $_SERVER["HTTP_REFERER"];
} elseif ('' !== $url && is_object($url)) {
$url = $url->build();
}
if (request()->isAjax()) {
return json([
'code' => 1,
'msg' => $msg,
'data' => $data,
'url' => $url
]);
} else {
return View::fetch('public/jump', [
'msg' => $msg,
'url' => $url,
'wait' => $wait,
'type' => 'success',
]);
}
}
public function error($msg = '操作失败', $url = '', $wait = 3, $data = [])
{
if (is_null($url) && isset($_SERVER["HTTP_REFERER"])) {
$url = $_SERVER["HTTP_REFERER"];
} elseif ('' !== $url && is_object($url)) {
$url = $url->build();
}
if (request()->isAjax()) {
return json([
'code' => 0,
'msg' => $msg,
'data' => $data,
'url' => $url
]);
} else {
return View::fetch('public/jump', [
'msg' => $msg,
'url' => $url,
'wait' => $wait,
'type' => 'error',
]);
}
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\facade\Cookie;
use think\facade\Db;
use think\facade\View;
class Contact extends Base
{
public function index()
{
if (Cookie::get('think_var') == 'en-us') {
View::assign('reason', CONTACT_REASON_EN);
} else {
View::assign('reason', CONTACT_REASON);
}
return View::fetch();
}
public function do_apply()
{
if(!captcha_check($_REQUEST['yzm'])){
return $this->error(lang('contact.yzm_error'));
}
if ($_REQUEST['email'] !== $_REQUEST['re_email']) {
return $this->error(lang('contact.email_error'));
}
unset($_REQUEST['re_email']);
unset($_REQUEST['s']);
unset($_REQUEST['yzm']);
$_REQUEST['reg_time'] = date('Y-m-d H:i:s');
$_REQUEST['source'] = MODEL;
$_REQUEST['model'] = CONTACT_MODEL;
$_REQUEST['lang'] = Cookie::get('think_var');
$_REQUEST['token'] = md5(CONTACT_MODEL);
try {
$order_res = Httpcurl::request(config('app.CREATE_CONTACT_URL'), 'post', $_REQUEST);
$order = json_decode($order_res[0], true);
if (!$order['code']) {
return $this->error($order['msg']);
}
return $this->success(lang('contact.success'));
} catch (\Exception $e) {
return $this->error(lang('contact.fail'));
}
}
}

View File

@@ -0,0 +1,24 @@
<?php
declare (strict_types = 1);
namespace app\home\controller;
use think\facade\View;
class Index extends Base
{
public function index()
{
return View::fetch();
}
public function langset()
{
$lang = input('lang') ? input('lang') : 'zh-cn';
parent::langs($lang);
return json([
'code' => 1,
'msg' => lang('controller.success'),
]);
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\exception\HttpResponseException;
use think\facade\Config;
use think\facade\Log;
use think\facade\Request;
use think\Response;
class Invoice extends Base
{
public function initialize()
{
parent::initialize();
if (!$this->_userId) {
throw new HttpResponseException(Response::create($this->error('请先登录', url('user/index'))));
}
}
/**个人中心
*/
public function index()
{
//获取用户的登记信息
$order_sn = Request::instance()->param('order_sn');
if (empty($order_sn)) {
return $this->error('参数错误');
}
$base_url = config('app.order_url');
$order_url = $base_url . 'getOrder';
$response = Httpcurl::request($order_url, 'post', ['order_sn' => $order_sn, 'model' => MODEL]);
if ($response[3]) {
return $this->error('异常错误,请重新提交');
}
$pay_result = json_decode($response[0], true);
if (empty($pay_result)) {
return $this->error('异常错误,请重新提交');
}
Log::info(json_encode($pay_result));
if (!$pay_result['code']) {
return $this->error($pay_result['msg']);
}
$data = $pay_result['data'];
if (empty($data)) {
return $this->error('无此数据');
}
$total_price = $data['total_price'] ? $data['total_price'] : $data['origin_price'];
$data['total_price'] = ($total_price / 100) . "";
return view('', [
'data' => $data,
]);
}
public function add_invoice()
{
$res = [
'status' => 0,
'msg' => '操作失败,请重新填写'
];
unset($_REQUEST['s']);
$_REQUEST['invoiceType'] = INCOICETYPE;
$_REQUEST['model'] = MODEL;
$_REQUEST['principals'] = config('app.principals');
$_REQUEST['uid'] = $this->_userId;
$base_url = config('app.order_url');
$order_url = $base_url . 'add_invoice';
$response = Httpcurl::request($order_url, 'post', $_REQUEST);
if ($response[3]) {
return json($res);
}
$pay_result = json_decode($response[0], true);
Log::info(json_encode($pay_result));
if (empty($pay_result)) {
return json($res);
}
if (!$pay_result['code']) {
$res['msg'] = $pay_result['msg'];
return json($res);
}
$res['status'] = 1;
$res['msg'] = $pay_result['msg'];
$res['url'] = url('user/index')->build();
return json($res);
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\exception\ValidateException;
use think\facade\Log;
use think\facade\Session;
class Login extends Base
{
public function login_code()
{
$param = $this->request->post();
try {
$this->validate($param, ['mobile' => 'require'], ['mobile.require' => '手机号不能为空']);
} catch (ValidateException $e) {
return $this->error($e->getError());
}
$code = mt_rand(1001, 9999);
//进行发送验证 走接口
$res = Httpcurl::request(config('app.YZM_URL'), 'post', [
'mobile' => $param['mobile'],
'code' => $code,
]);
$result = json_decode($res[0], true);
Log::info(json_encode($result));
if (!$result['code']) {
return $this->error('发送验证码失败');
}
//进行记录
if ($result['code'] == 1) {
//把内容存入session
Session::set($param['mobile'] . '_code', $code);
Session::set($param['mobile'] . '_' . $code, time());
return $this->success('发送成功');
} else {
return $this->error('发送失败');
}
}
// 登录
public function login()
{
$param = $this->request->post();
try {
$this->validate($param, ['phone' => 'require', 'vercode' => 'require'], ['phone.require' => '手机号不能为空', 'vercode.require' => '验证码不能为空']);
} catch (ValidateException $e) {
return $this->error($e->getError());
}
$mobile = $param['phone'];
$code = $param['vercode'];
$res = 0;
$sms = Session::get($mobile . '_code');
if (empty($sms)) {
return $this->error('登录失败');
}
if ($sms != $code) {
return $this->error('验证码不正确');
}
$time = time();
$code_time = Session::get($mobile . '_' . $code);
//判断是否已经超时5分钟
if (($time - $code_time) > 300) {
Session::delete($mobile . '_code');
Session::delete($mobile . '_' . $code);
return $this->error('验证码超时,请重新发送');
}
//查询账号
$user_url = config('app.user_url');
$res = Httpcurl::request($user_url, 'post', [
'model' => MODEL,
'mobile' => $mobile,
]);
$result = json_decode($res[0], true);
Log::info(json_encode($result));
if (!$result['code']) {
return $this->error('账号不存在');
}
Session::set('user', $result['data']);
return $this->success('登录成功', url('user/index'));
}
public function logout()
{
Session::delete('user');
return redirect('/home/index');
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\facade\Log;
use think\facade\View;
class Lookup extends Base
{
public function index()
{
return View::fetch();
}
public function enrollment()
{
$data = request()->post();
$map = [
'passport_number' => $data['passport_number'],
'birth_date' => $data['birth_date'],
'last_name' => $data['last_name'],
'first_name' => $data['first_name'],
'model' => MODEL,
];
$res = Httpcurl::request(config('app.LOOKUP_URL'), 'post', $map);
$res = json_decode($res[0], true);
Log::info(json_encode($res));
if (empty($res)) {
return json(['code' => 0, 'msg' => lang("controller.no_records_found")]);
}
if ($res['code'] == 0) {
return json(['code' => 0, 'msg' => $res['msg']]);
}
return json(['code' => 1, 'msg' => $res['msg'], 'data' => $res['data']]);
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\facade\Log;
use think\facade\View;
use think\Request;
class News extends Base
{
public function index()
{
$page = input('page') ? input('page') : 1;
$size = input('size') ? input('size') : 10;
$news_list = Httpcurl::request(config('app.NEWS_LIST'), 'post', [
'type' => MODEL,
'company'=>COMPANY_TITLE,
'page' => $page,
'size' => $size,
]);
$news_list = json_decode($news_list[0], true);
Log::info(json_encode($news_list));
if ($news_list['code'] == 1) {
View::assign('news_list', $news_list['data']);
View::assign('count', $news_list['count']);
View::assign('page', $page);
View::assign('size', $size);
} else {
View::assign('news_list', []);
}
return View::fetch('news');
}
public function detail(Request $request)
{
$data = $request->param();
if (empty($data['id'])) {
return $this->error('参数错误');
}
$info = Httpcurl::request(config('app.NEWS_DETAIL'), 'post', [
'type' => MODEL,
'id' => $data['id'],
]);
$info = json_decode($info[0], true);
Log::info(json_encode($info));
$info_detail = [];
if ($info['code'] == 1) {
$info_detail = $info['data'];
}
View::assign('info', $info_detail);
return View::fetch('news_detail');
}
}

123
app/home/controller/Pay.php Normal file
View File

@@ -0,0 +1,123 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\exception\HttpResponseException;
use think\facade\Db;
use think\facade\Log;
use think\facade\Request;
use think\facade\Session;
use think\facade\View;
use think\Response;
class Pay extends Base
{
private $order_sn ;
public function initialize()
{
parent::initialize();
$order_sn = input('order_sn');
if (empty($order_sn)) {
$order_sn = input('reference');
}
if (!$order_sn) {
throw new HttpResponseException(Response::create($this->error(lang("controller.missing_order_sn"), url('visa/index'))));
}
$this->order_sn = $order_sn;
}
public function pay()
{
/*查询订单*/
$order_info = Httpcurl::request(config('app.GET_ORDER_DETAIL_URL'), 'post', ['order_sn' => $this->order_sn, 'model' => MODEL]);
$order_info = json_decode($order_info[0], true);
if ($order_info['code'] == '0') {
return $this->error(lang("controller.order_not_exist"), url('visa/index'));
}
$order_info = $order_info['data'];
if (empty($order_info)) {
return $this->error(lang("controller.order_not_exist"), url('visa/index'));
}
/*判断订单状态*/
if ($order_info['pay_status'] == 1 ) {
return $this->success(lang("controller.payment_success"), url('visa/index'));
}
/*判断订单状态*/
$params = [
'amount' => $order_info['total_price'],
'terminal' => $order_info['is_mobile'] == '1' ? 'WAP' : 'PC',
'order_sn' => $order_info['order_sn'],
'vendor' => PAY_TYPE[$order_info['pay_type']],
'note' => PAYMODEL,
];
$response = HttpCurl::request(config('app.NI_HAO_PAY_URL'), 'post', $params);
Log::info(json_encode($response));
$response = json_decode($response[0], true);
if ($response['code'] != 1) {
return $this->error($response['msg']);
}
echo $response['data'];exit();
}
public function succ()
{
/*判断订单状态*/
$order_info = Httpcurl::request(config('app.GET_ORDER_DETAIL_URL'), 'post', ['order_sn' => $this->order_sn, 'model' => MODEL]);
$order_info = json_decode($order_info[0], true);
if ($order_info['code'] == '0') {
return $this->error(lang("controller.order_not_exist"));
}
$order_info = $order_info['data'];
if ($order_info) {
if ($order_info['pay_status'] == 0 ) {
return $this->success(lang("controller.payment_not_completed"), url('visa/index'));
}
}
/*判断订单状态*/
$arrive_date = $order_info['arrive_date'] ?? '';
$arrive_timestamp = $arrive_date ? strtotime($arrive_date) : false;
$latest_submit_timestamp = strtotime(date('Y-m-d') . ' +' . ARRIVE_DATE_COUNT . ' days 23:59:59');
if ($arrive_timestamp === false || $arrive_timestamp < $latest_submit_timestamp) {
View::assign('arrive_date_info', '');
} else {
$arrive_date_info = sprintf(lang("pay.arrive_date_info"), $arrive_date, date('Y-m-d', $arrive_timestamp - ARRIVE_DATE_COUNT * 86400));
View::assign('arrive_date_info', $arrive_date_info);
}
View::assign('payment_confirmed', true);
View::assign('poll_status_url', '');
View::assign('repay_url', '');
View::assign('order_sn', $this->order_sn);
return View::fetch('success');
}
public function h5result()
{
if (Request::instance()->isAjax()) {
/*查询订单*/
$order_info = Httpcurl::request(config('app.GET_ORDER_DETAIL_URL'), 'post', ['order_sn' => $this->order_sn, 'model' => MODEL]);
$order_info = json_decode($order_info[0], true);
if ($order_info['code'] == '0') {
return json([
'status' => 0,
'msg' => lang("controller.order_not_exist_retry"),
'url' => url('visa/index')->build()
]);
}
$order_info = $order_info['data'];
/*判断订单状态*/
if ($order_info['pay_status'] == 1) {
return json([
'status' => 1,
'msg' => lang("controller.payment_success"),
'url' => url('pay/succ', ['reference' => $this->order_sn])->build()
]);
}
return json([
'status' => 0,
'msg' => lang("controller.payment_not_completed_retry"),
'url' => url('pay/pay', ['order_sn' => $this->order_sn])->build()
]);
}
View::assign('order_sn', $this->order_sn);
return View::fetch();
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace app\home\controller;
use think\facade\View;
class Policy extends Base
{
public function cookie()
{
return View::fetch();
}
public function disclaimer()
{
return View::fetch();
}
public function privacy()
{
return View::fetch();
}
public function pay()
{
return View::fetch();
}
public function refund()
{
return View::fetch();
}
public function user()
{
return View::fetch();
}
}

View File

@@ -0,0 +1,128 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\exception\HttpResponseException;
use think\facade\Db;
use think\facade\Request;
use think\facade\Session;
use think\facade\View;
use think\Response;
class Sqpay extends Base
{
private $order_sn ;
public function initialize()
{
parent::initialize();
if (!input('order_sn')) {
throw new HttpResponseException(Response::create($this->error(lang("controller.missing_order_sn"), url('visa/index'))));
}
$this->order_sn = input('order_sn');
}
public function pay()
{
$order_info = Httpcurl::request(config('app.GET_ORDER_DETAIL_URL'), 'post', ['order_sn' => $this->order_sn, 'model' => MODEL]);
$order_info = json_decode($order_info[0], true);
if ($order_info['code'] == '0') {
return json([
'status' => 0,
'msg' => lang("controller.order_not_exist"),
'url' => url('visa/index')->build()
]);
}
$order_info = $order_info['data'];
if (empty($order_info)) {
return $this->error(lang("controller.order_not_exist"), url('visa/index'));
}
/*判断订单状态*/
if ($order_info['pay_status'] == 1 ) {
return $this->success(lang("controller.payment_success"), url('visa/index'));
}
/*判断订单状态*/
View::assign('order_sn', $order_info['order_sn']);
View::assign('total_fee', number_format($order_info['total_price'] / 100, 2));
View::assign('total_price', $order_info['total_price']);
View::assign('order_info', $order_info);
View::assign('applicationId', config('app.SQ_CONIG')['app_id']);
View::assign('locationId', config('app.SQ_CONIG')['location_id']);
View::assign('pay_country', config('app.SQ_COUNTRY'));
View::assign('currency', config('app.SQ_CURRENCY'));
View::assign('uid', uniqid());
return View::fetch();
}
public function paySquare()
{
$request_body = file_get_contents('php://input');
$data = json_decode($request_body, true);
$data['note'] = MODEL;
$response = HttpCurl::request(config('app.SQ_PAY_URL'), 'post', $data);
if ($response[3]) {
return json(['code' => 0, 'msg' => lang("controller.payment_not_completed")]);
}
$pay_result = json_decode($response[0], true);
if ($pay_result) {
if ($pay_result['code'] == 1) {
return json(['code' => 1, 'msg' => lang("controller.payment_success"), 'data' => $pay_result['msg'], 'url' => url('sqpay/succ',['order_sn'=>$this->order_sn])->build()]);
} else {
return json(['code' => 0, 'msg' => lang("controller.payment_not_completed")]);
}
}
}
private function getOrderDetail()
{
$order_info = Httpcurl::request(config('app.GET_ORDER_DETAIL_URL'), 'post', ['order_sn' => $this->order_sn, 'model' => MODEL]);
return json_decode($order_info[0], true);
}
public function payStatus()
{
$order_info = $this->getOrderDetail();
if (($order_info['code'] ?? '0') == '0') {
return json([
'status' => -1,
'msg' => lang("controller.order_not_exist"),
'url' => url('visa/index')->build()
]);
}
$order_data = $order_info['data'] ?? [];
$is_paid = ($order_data['pay_status'] ?? 0) == 1 ;
return json([
'status' => $is_paid ? 1 : 0,
'msg' => $is_paid ? lang("controller.payment_success") : lang("sqpay.notice_text"),
'url' => $is_paid ? url('sqpay/succ', ['order_sn' => $this->order_sn])->build() : url('sqpay/pay', ['order_sn' => $this->order_sn])->build()
]);
}
public function succ()
{
$order_info = $this->getOrderDetail();
if (($order_info['code'] ?? '0') == '0') {
return $this->error(lang("controller.order_not_exist"));
}
$order_info = $order_info['data'] ?? [];
$payment_confirmed = ($order_info['pay_status'] ?? 0) == 1 ;
$arrive_date = $order_info['arrive_date'] ?? '';
$arrive_timestamp = $arrive_date ? strtotime($arrive_date) : false;
$latest_submit_timestamp = strtotime(date('Y-m-d') . ' +' . ARRIVE_DATE_COUNT . ' days 23:59:59');
if ($arrive_timestamp === false || $arrive_timestamp < $latest_submit_timestamp) {
View::assign('arrive_date_info', '');
} else {
$arrive_date_info = sprintf(lang("pay.arrive_date_info"), $arrive_date, date('Y-m-d', $arrive_timestamp - ARRIVE_DATE_COUNT * 86400));
View::assign('arrive_date_info', $arrive_date_info);
}
View::assign('payment_confirmed', $payment_confirmed);
View::assign('poll_status_url', url('sqpay/payStatus', ['order_sn' => $this->order_sn])->build());
View::assign('repay_url', url('sqpay/pay', ['order_sn' => $this->order_sn])->build());
View::assign('order_sn', $this->order_sn);
return View::fetch('pay/success');
}
}

View File

@@ -0,0 +1,199 @@
<?php
declare (strict_types=1);
namespace app\home\controller;
use app\service\AwsUploadService;
use app\service\BaiduOcrService;
use app\service\CompressImgService;
use app\service\SwooleService;
use think\facade\Log;
use think\file\UploadedFile;
class Upload extends Base
{
public function upload_passport()
{
// 获取上传的文件
$file = request()->file('file');
$path = request()->param('path', '');
if (!$file) {
return json([
'code' => 0,
'msg' => lang('controller.upload_missing'),
]);
}
$imgdata = file_get_contents($file->getPathname());
/*同时上传护照文件*/
$fileName = $path . '/' . date('YmdHis') . uniqid() . '.' . pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
try {
$client = SwooleService::getInstance();
$client->sendTask([
'type' => 'upload_ali',
'data' => [
'Bucket' => BUCKET,
'Key' => $fileName, //重命名的文件名
'file_content' => base64_encode($imgdata),
]
]);
$client->close();
return json([
'code' => 1,
'msg' => lang('controller.upload_success'),
'path' => OSS_URL . $fileName,
]);
} catch (\Exception $e) {
return json([
'code' => 0,
'msg' => lang('controller.upload_failed'),
'path' => ''
]);
}
}
public function visa()
{
// 获取上传的文件
$file = request()->file('file');
$path = request()->param('path', '');
if (!$file) {
return json([
'code' => 0,
'msg' => lang('controller.upload_missing'),
]);
}
$imgdata = file_get_contents($file->getPathname());
/*同时上传签证文件*/
$fileName = $path . '/' . date('YmdHis') . uniqid() . '.' . pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
try {
$client = SwooleService::getInstance();
$client->sendTask([
'type' => 'upload_ali',
'data' => [
'Bucket' => BUCKET,
'Key' => $fileName, //重命名的文件名
'file_content' => base64_encode($imgdata),
]
]);
$client->close();
return json([
'code' => 1,
'msg' => lang('controller.upload_success'),
'path' => OSS_URL . $fileName,
]);
} catch (\Exception $e) {
return json([
'code' => 0,
'msg' => lang('controller.upload_failed'),
'path' => ''
]);
}
}
public function identification()
{
// 获取上传的文件
$file = request()->file('file');
$path = request()->param('path', '');
if (!$file) {
return json([
'code' => 0,
'msg' => lang('controller.upload_missing'),
]);
}
$imgdata = file_get_contents($file->getPathname());
/*同时上传身份证文件*/
$fileName = $path . '/' . date('YmdHis') . uniqid() . '.' . pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
try {
$client = SwooleService::getInstance();
$client->sendTask([
'type' => 'upload_ali',
'data' => [
'Bucket' => BUCKET,
'Key' => $fileName, //重命名的文件名
'file_content' => base64_encode($imgdata),
]
]);
$client->close();
return json([
'code' => 1,
'msg' => lang('controller.upload_success'),
'path' => OSS_URL . $fileName,
]);
} catch (\Exception $e) {
return json([
'code' => 0,
'msg' => lang('controller.upload_failed'),
'path' => ''
]);
}
}
public function awsUpload($file, $fileName)
{
$fileSize = $file->getSize();
$originalName = $file->getOriginalName();
$extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
$sizeLimit = 3 * 1024 * 1024;
if ($fileSize > $sizeLimit && in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) {
$compressedFilePath = (new CompressImgService())->compressImage($file->getRealPath(), $fileSize);
$file = new UploadedFile(
$compressedFilePath,
pathinfo($originalName, PATHINFO_FILENAME) . '_compressed.' . $extension,
mime_content_type($compressedFilePath) ?: $file->getMime(),
0,
true
);
}
$service = new AwsUploadService();
// 额外传递的字段
$extraFields = [
'Bucket' => BUCKET,
'Key' => $fileName //重命名的文件名
];
$result = $service->uploadAndForward($file, 'file', $extraFields);
Log::info(json_encode($result));
return $result;
}
public function file_upload()
{
// 获取上传的文件
$file = request()->file('file');
$path = request()->param('path', '');
if (!$file) {
return json([
'code' => 0,
'msg' => lang('controller.upload_missing'),
]);
}
$imgdata = file_get_contents($file->getPathname());
$fileName = $path . '/' . date('YmdHis') . uniqid() . '.' . pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
try {
$client = SwooleService::getInstance();
$client->sendTask([
'type' => 'upload_ali',
'data' => [
'Bucket' => BUCKET,
'Key' => $fileName, //重命名的文件名
'file_content' => base64_encode($imgdata),
]
]);
$client->close();
return json([
'code' => 1,
'msg' => lang('controller.upload_success'),
'path' => OSS_URL . $fileName,
]);
} catch (\Exception $e) {
return json([
'code' => 0,
'msg' => lang('controller.upload_failed'),
'path' => '',
]);
}
}
}

View File

@@ -0,0 +1,200 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\facade\Log;
use think\facade\Request;
class User extends Base
{
public function index()
{
if (!empty($this->_userId)) {
$paystatus = Request::instance()->param('paystatus', '');
$paystatus = $paystatus ? $paystatus : 'pay';
if (!in_array($paystatus, ['pay', 'nopay'])) {
return $this->error('参数错误');
}
$list = Httpcurl::request(config('app.GET_ORDER_URL'), 'post', [
'model' => MODEL,
'user_id' => $this->_userId,
'invoiceType' => INCOICETYPE,
'pay_status' => '',
]);
$result = json_decode($list[0], true);
Log::info(json_encode($result));
if (!$result['code']) {
return $this->error('获取订单失败');
}
return view('', [
'paystauts' => $paystatus,
'order_list' => $result['data'],
]);
} else {
return view('login');
}
}
public function invoice()
{
return view('invoice');
}
public function multiple()
{
return view('multiple');
}
public function checkInvoice()
{
$res = [
'status' => 0,
'msg' => '操作失败'
];
$passport_number = Request::instance()->param('passport_number');
$order_sn = Request::instance()->param('order_sn');
$last_name = Request::instance()->param('last_name');
$first_name = Request::instance()->param('first_name');
if (empty($passport_number) || empty($order_sn) || empty($last_name) || empty($first_name)) {
$res['msg'] = '参数错误';
return json($res);
}
$base_url = config('app.order_url');
$order_url = $base_url . 'checkInvoice';
$response = Httpcurl::request($order_url, 'post', [
'passport_number' => trim($passport_number),
'order_sn' => trim($order_sn),
'last_name' => trim($last_name),
'first_name' => trim($first_name),
'model' => MODEL
]);
if ($response[3]) {
$res['msg'] = '参数错误';
return json($res);
}
$data = json_decode($response[0], true);
Log::info(json_encode($data));
if (empty($data)) {
$res['msg'] = '系统错误,请联系管理员!';
return json($res);
}
if (!$data['code']) {
$res['msg'] = "暂无订单信息";
return json($res);
}
$invoice_url = $base_url . 'getInvoice';
//判断是否已经开票了
$response = Httpcurl::request($invoice_url, 'post', [
'order_sn' => $order_sn,
'invoiceType' => INCOICETYPE,
'model' => MODEL
]);
if ($response[3]) {
$res['msg'] = '参数错误';
return json($res);
}
$order = json_decode($response[0], true);
Log::info(json_encode($order));
if (empty($order)) {
$res['msg'] = '系统错误,请联系管理员!';
return json($res);
}
if (!$order['code']) {
return json($res);
}
if (!empty($order['data'])) {
$res['msg'] = '已经开票';
return json($res);
}
$res = [
'status' => 1,
'msg' => '操作成功',
'url' => url('home/invoice/index', ['order_sn' => $order_sn])->build()
];
return json($res);
}
public function checkMultiple()
{
$res = [
'status' => 0,
'msg' => '操作失败'
];
$datas = Request::instance()->param('datas');
if (empty($datas)) {
$res['msg'] = '参数错误';
return json($res);
}
$datas = json_decode(html_entity_decode(stripslashes($datas)), true);
if (empty($datas)) {
$res['msg'] = '数据错误';
return json($res);
}
$order_sns = array_column($datas, 'order_sn');
//判断是否有重复订单
if (count($order_sns) != count(array_unique($order_sns))) {
$res['msg'] = '有重复订单,请删除后重新提交';
return json($res);
}
$base_url = config('app.order_url');
$order_url = $base_url . 'checkInvoice';
$invoice_url = $base_url . 'getInvoice';
foreach ($datas as $da) {
//获取信息
$response = Httpcurl::request($order_url, 'post', [
'passport_number' => trim($da['passport_number']),
'order_sn' => trim($da['order_sn']),
'last_name' => trim($da['last_name']),
'first_name' => trim($da['first_name']),
'model' => MODEL
]);
if ($response[3]) {
$res['msg'] = '参数错误';
return json($res);
}
$data = json_decode($response[0], true);
Log::info(json_encode($data));
if (empty($data)) {
$res['msg'] = '未查到订单信息';
return json($res);
}
//查询是否已经开票了
$response = Httpcurl::request($invoice_url, 'post', [
'order_sn' => trim($da['order_sn']),
'invoiceType' => INCOICETYPE,
'model' => MODEL
]);
if ($response[3]) {
$res['msg'] = '参数错误';
return json($res);
}
$order = json_decode($response[0], true);
Log::info(json_encode($order));
if (empty($order)) {
$res['msg'] = '系统错误,请联系管理员!';
return json($res);
}
if (!$order['code']) {
$res['msg'] = $order['msg'];
return json($res);
}
if (!empty($order['data'])) {
$res['msg'] = '订单号:' . $da['order_sn'] . '已经开票';
return json($res);
}
}
$order_sns = implode('-', $order_sns);
$res = [
'status' => 1,
'msg' => '操作成功',
'url' => url('home/multiple/index', ['id' => base64_encode($order_sns)])->build()
];
return json($res);
}
}

View File

@@ -0,0 +1,160 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\facade\Cache;
use think\facade\Cookie;
use think\facade\Log;
use think\facade\Session;
use think\facade\View;
use think\Request;
class Visa extends Base
{
public $provinces = [
'安徽' => 'ANHUI',
'北京' => 'BEIJING',
'重庆' => 'CHONGQING',
'福建' => 'FUJIAN',
'甘肃' => 'GANSU',
'广东' => 'GUANGDONG',
'广西壮族自治区' => 'GUANGXI ZHUANG',
'贵州' => 'GUIZHOU',
'海南' => 'HAINAN',
'河北' => 'HEBEI',
'黑龙江' => 'HEILONGJIANG',
'河南' => 'HENAN',
'香港特别行政区' => 'HONG KONG SAR',
'湖北' => 'HUBEI',
'湖南' => 'HUNAN',
'内蒙古自治区' => 'INNER MONGOLIA',
'江苏' => 'JIANGSU',
'江西' => 'JIANGXI',
'吉林' => 'JILIN',
'辽宁' => 'LIAONING',
'澳门特别行政区' => 'MACAU SAR',
'宁夏回族自治区' => 'NINGXIA HUI',
'青海' => 'QINGHAI',
'陕西' => 'SHAANXI',
'山东' => 'SHANDONG',
'上海' => 'SHANGHAI',
'山西' => 'SHANXI',
'四川' => 'SICHUAN',
'台湾' => 'TAIWAN',
'天津' => 'TIANJIN',
'新疆维吾尔自治区' => 'XINJIANG',
'西藏自治区' => 'XIZANG',
'云南' => 'YUNNAN',
'浙江' => 'ZHEJIANG'
];
public function index()
{
View::assign('th_province', json_decode(file_get_contents(CONFIG_JSON_TH_PROVINCE), true));
View::assign('nation', json_decode(file_get_contents(CONFIG_JSON_VISA_NATIONALITY), true));
if (Cookie::get('think_var') == 'en-us') {
View::assign('travel_country', json_decode(file_get_contents(CONFIG_JSON_COUNTRY_EN), true));
} else {
View::assign('travel_country', json_decode(file_get_contents(CONFIG_JSON_COUNTRY), true));
}
return View::fetch();
}
public function getCountry()
{
if (Cookie::get('think_var') == 'en-us') {
$birth_country = file_get_contents(CONFIG_JSON_COUNTRY_EN);
} else {
$birth_country = file_get_contents(CONFIG_JSON_COUNTRY);
}
return $this->success('成功', '', json_decode($birth_country, true));
}
public function get_sub_city(Request $request)
{
$id = $request->param('id');
$city = find_json(CONFIG_JSON_TH_CITY, 'provinceCode', $id);
if ($city) {
return $this->success('成功', '', $city);
} else {
return $this->success('成功', '', []);
}
}
public function get_sub_istrict(Request $request)
{
$id = $request->param('id');
$city = find_json(CONFIG_JSON_TH_DISTRICT, 'districtCode', $id);
if ($city) {
return $this->success('成功', '', $city);
} else {
return $this->success('成功', '', []);
}
}
public function get_common_json($json_name, $field, $value, $flag, $key)
{
$data = find_json($json_name, $field, $value);
if ($data) {
if ($flag == 1) {
return $data;
} else {
return $data[0][$key];
}
} else {
return [];
}
}
public function getprovince($data)
{
if (array_key_exists($data, $this->provinces)) {
return $this->provinces[$data];
} else {
return $data;
}
}
public function do_apply()
{
$request = $this->request;
$data = $request->post();
$order_sn = getOrderNumber();
unset($data['check1']);
unset($data['check2']);
unset($data['visit_info1']);
$data['model'] = MODEL;
$data['token'] = md5(MODEL);
$data['source'] = Session::get('source');
$data['order_sn'] = $order_sn;
$data['is_mobile'] = isMobile();
$data['live_city'] = $this->getprovince($data['live_city']);
/*处理 map */
$nationality_map = json_decode(file_get_contents(CONFIG_JSON_NATIONALITY_MAP), true) ?? [];
$data['nation'] = $nationality_map[$data['nation']] ?? $data['nation'];
$order_res = Httpcurl::request(config('app.CREATE_ORDER_URL'), 'post', $data);
$order = json_decode($order_res[0], true);
Log::info(json_encode($order));
if (!$order['code']) {
return $this->error(lang('controller.submit_failed'));
}
Session::set('order_sn', $order_sn);
Session::set('total_price', $data['total_price']);
$url = url('wepay/pcpay', ['order_sn' => $order_sn]);
if (isMobile()) {
if (strpos($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') !== false) {
$url = url('wepay/wxpay', ['order_sn' => $order_sn]);
}else{
$url = url('wepay/h5pay', ['order_sn' => $order_sn]);
}
}
return $this->success(lang('controller.success'), $url);
/*if ($_REQUEST['pay_type'] == '3') {
return $this->success(lang('controller.success'), url('sqpay/pay',['order_sn'=>$order_sn]));
} else {
return $this->success(lang('controller.submit_success'), url('pay/pay',['order_sn'=>$order_sn]), '1');
}*/
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\facade\Request;
class Wechatpay extends Base
{
public function index()
{
$order_sn = Request::instance()->param('order_sn');
if (!$order_sn) {
return $this->error(lang('wepay.missing_order_sn'), url('visa/index'));
}
$response = Httpcurl::request(config('app.GET_ORDER_DETAIL_URL'), 'post', ['order_sn' => $order_sn, 'model' => MODEL]);
if ($response[3]) {
return $this->error(lang('wepay.payment_failed_retry'), url('visa/index'));
}
$pay_result = json_decode($response[0], true);
if (empty($pay_result)) {
return $this->error(lang('wepay.payment_failed_retry'), url('visa/index'));
}
if (!$pay_result['code']) {
return $this->error(lang('wepay.payment_failed_retry'), url('visa/index'));
}
$pay_result = $pay_result['data'];
if ($pay_result['pay_status']) {
return $this->success(lang('wepay.payment_success'), url('wepay/succ'), ['order_sn' => $order_sn]);
}
return $this->error(lang('wepay.payment_failed_retry'), url('visa/index'));
}
}

View File

@@ -0,0 +1,185 @@
<?php
namespace app\home\controller;
use api\Httpcurl;
use think\exception\HttpResponseException;
use think\facade\Log;
use think\facade\Request;
use think\facade\View;
use think\Response;
class Wepay extends Base
{
private $order_sn;
public function initialize()
{
parent::initialize();
$order_sn = input('order_sn');
if (empty($order_sn)) {
$order_sn = input('reference');
}
if (!$order_sn) {
throw new HttpResponseException(Response::create($this->error(lang('wepay.missing_order_sn'), url('visa/index'))));
}
$this->order_sn = $order_sn;
}
public function wxpay()
{
$wechaturl = config('app.wechatpay');
$wechaturl .= '?order_sn=' . $this->order_sn . '&mark=' . PAY_MARK . '&body=' . urlencode(COUNTRY . lang('wepay.pay_body'));
header('location:' . $wechaturl);
exit;
}
public function pcpay()
{
$order_sn = $this->order_sn;
$response = Httpcurl::request(config('app.GET_ORDER_DETAIL_URL'), 'post', ['order_sn' => $order_sn, 'model' => MODEL]);
if ($response[3]) {
return $this->error(lang('wepay.system_error'), url('visa/index'));
}
$pay_result = json_decode($response[0], true);
Log::info(json_encode($pay_result));
if (empty($pay_result)) {
return $this->error(lang('wepay.system_error'), url('visa/index'));
}
if (!$pay_result['code']) {
return $this->error($pay_result['msg'], url('visa/index'));
}
$pay_result = $pay_result['data'];
if ($pay_result['pay_status']) {
return $this->success(lang('wepay.already_paid'), url('visa/index'));
}
$body = COUNTRY . lang('wepay.pay_body');
$wxurl = '';
$res = Httpcurl::request(config('app.wechatpcpay'), 'get', ['order_sn' => $order_sn, 'mark' => PAY_MARK, 'body' => $body]);
if (!$res[3]) {
$result = json_decode($res[0], true);
$wxurl = $result['url'] ?? '';
}
Log::info(json_encode($res));
$aliurl = '';
$res = Httpcurl::request(config('app.alipcpay'), 'get', ['order_sn' => $order_sn, 'mark' => PAY_MARK, 'body' => urlencode($body)]);
if (!$res[3]) {
$result = json_decode($res[0], true);
$aliurl = $result['url'] ?? '';
}
Log::info(json_encode($res));
View::assign('url', $wxurl);
View::assign('aliurl', $aliurl);
View::assign('order_sn', $order_sn);
View::assign('total_fee', number_format(($pay_result['total_price'] ?? 0) / 100, 2));
return View::fetch();
}
public function h5pay()
{
$wechaturl = config('app.wechath5pay');
$wechaturl .= '?order_sn=' . $this->order_sn . '&mark=' . PAY_MARK . '&body=' . urlencode(COUNTRY . lang('wepay.pay_body'));
header('location:' . $wechaturl);
exit;
}
public function succ()
{
/*判断订单状态*/
$order_info = Httpcurl::request(config('app.GET_ORDER_DETAIL_URL'), 'post', ['order_sn' => $this->order_sn, 'model' => MODEL]);
$order_info = json_decode($order_info[0], true);
if ($order_info['code'] == '0') {
return $this->error(lang("controller.order_not_exist"));
}
$order_info = $order_info['data'];
if ($order_info) {
if ($order_info['pay_status'] == 0 ) {
return $this->success(lang("controller.payment_not_completed"), url('visa/index'));
}
}
/*判断订单状态*/
$arrive_date = $order_info['arrive_date'] ?? '2026-05-30';
$arrive_timestamp = $arrive_date ? strtotime($arrive_date) : false;
$latest_submit_timestamp = strtotime(date('Y-m-d') . ' +' . ARRIVE_DATE_COUNT . ' days 23:59:59');
if ($arrive_timestamp === false || $arrive_timestamp < $latest_submit_timestamp) {
View::assign('arrive_date_info', '');
} else {
$arrive_date_info = sprintf(lang("pay.arrive_date_info"), $arrive_date, date('Y-m-d', $arrive_timestamp - ARRIVE_DATE_COUNT * 86400));
View::assign('arrive_date_info', $arrive_date_info);
}
View::assign('payment_confirmed', true);
View::assign('poll_status_url', '');
View::assign('repay_url', '');
View::assign('order_sn', $this->order_sn);
return View::fetch();
}
public function h5result()
{
if (!Request::instance()->isAjax()) {
return View::fetch();
}
return $this->checkPayStatus('h5pay');
}
public function pcresult()
{
if (!Request::instance()->isAjax()) {
return null;
}
return $this->checkPayStatus('pcpay');
}
private function checkPayStatus(string $retryAction)
{
$order_sn = $this->order_sn;
$response = Httpcurl::request(config('app.GET_ORDER_DETAIL_URL'), 'post', ['order_sn' => $order_sn, 'model' => MODEL]);
if ($response[3]) {
return json([
'status' => 0,
'msg' => lang('wepay.system_error'),
'url' => url('wepay/index', ['order_sn' => $order_sn])->build(),
]);
}
$pay_result = json_decode($response[0], true);
Log::info(json_encode($pay_result));
if (empty($pay_result)) {
return json([
'status' => 0,
'msg' => lang('wepay.system_error'),
'url' => url('wepay/index', ['order_sn' => $order_sn])->build(),
]);
}
if (!$pay_result['code']) {
return json([
'status' => 0,
'msg' => $pay_result['msg'],
'url' => url('wepay/index', ['order_sn' => $order_sn])->build(),
]);
}
$pay_result = $pay_result['data'];
if ($pay_result['pay_status']) {
return json([
'status' => 1,
'msg' => lang('wepay.payment_success'),
'url' => url('wepay/succ', ['order_sn' => $order_sn])->build(),
]);
}
return json([
'status' => 0,
'msg' => lang('wepay.payment_not_completed'),
'url' => url('wepay/' . $retryAction, ['order_sn' => $order_sn])->build(),
]);
}
}

5
app/home/event.php Normal file
View File

@@ -0,0 +1,5 @@
<?php
// 这是系统自动生成的event定义文件
return [
];

5
app/home/middleware.php Normal file
View File

@@ -0,0 +1,5 @@
<?php
// 这是系统自动生成的middleware定义文件
return [
];

View File

@@ -0,0 +1,441 @@
{include file="public/head" /}
<link rel="stylesheet" href="/static/css/visa.css?v={:time()}">
<style>
body { background-color: #f5f5f5; }
/* ===== 页面容器:居中卡片风格 ===== */
.contact-card{
background: #fff;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,.06);
padding: 28px 30px 22px;
border: 1px solid rgba(0,0,0,.06);
}
.contact-title h2{
font-size: 34px;
font-weight: 800;
color: #111;
margin: 0 0 10px;
}
.contact-desc{
color: #666;
font-size: 14px;
margin: 0 0 18px;
}
.contact-hr{
height: 1px;
background: #eee;
margin: 16px 0 22px;
}
/* ===== 表单:标签在上(截图同款) ===== */
.layui-form-item{ margin-bottom: 18px; }
.layui-form-label{
float: none;
width: 100%;
text-align: left;
padding: 0 0 8px 0;
font-weight: 700;
color: #222;
}
.layui-input-block{
margin-left: 0;
min-height: auto;
}
.layui-form-label span{
color: #e53935;
margin-left: 4px;
font-weight: 800;
}
/* 输入框更接近截图(高度、边框、圆角) */
.layui-input, .layui-textarea, .layui-select{
border-radius: 4px;
border-color: #e6e6e6;
}
.layui-input{ height: 44px; line-height: 44px; }
.layui-textarea{ min-height: 150px; padding: 10px 12px; }
/* ===== 输入组:做成 flex验证码在右侧不挤压 ===== */
.layui-input-group{
display: flex;
align-items: center;
gap: 12px;
width: 100%;
}
.layui-input-group > .layui-input,
.layui-input-group > input.layui-input{
flex: 1 1 auto;
min-width: 0;
}
.layui-input-group .layui-input-group-prepend{
flex: 0 0 auto;
width: auto;
}
.captcha-img-container{
display: flex;
align-items: center;
gap: 10px;
white-space: nowrap;
}
.captcha-img-container img{
height: 44px;
border-radius: 4px;
}
.captcha-img-container a{
color: #1e88e5;
font-size: 13px;
}
/* radio 更靠近截图:一行内更紧凑 */
.layui-form-radio{ margin: 0 18px 0 0; }
/* 提交按钮居中、接近你导航蓝色 */
.contact-actions{ margin-top: 8px; }
.contact-actions .layui-btn{
height: 44px;
line-height: 44px;
padding: 0 34px;
border-radius: 4px;
background: #1e73b8;
font-weight: 700;
}
@media (max-width: 740px) {
.contact-card{ padding: 22px 16px 18px; }
.contact-title h2{ font-size: 28px; }
}
/* 只影响验证码这一行 */
.captcha-item .layui-input-group{
justify-content: flex-start; /* 不把验证码推到最右 */
}
/* 固定验证码输入框宽度(你可以改成 180/200/220 */
.captcha-item .layui-input-group > input.layui-input{
flex: 0 0 220px;
max-width: 220px;
}
/* 小屏幕下恢复全宽并换行更舒服 */
@media (max-width: 740px){
.captcha-item .layui-input-group{
flex-wrap: wrap;
gap: 10px;
}
.captcha-item .layui-input-group > input.layui-input{
flex: 1 1 100%;
max-width: 100%;
}
}
</style>
<body>
<div class="zh_content">
{include file="public/newnav" /}
</div>
<div class="layui-container contact-page">
<div class="contact-card">
<div class="contact-title">
<h2>{:lang('contact.menu_contact')}</h2>
</div>
<p class="contact-desc">
{:lang('contact.info')}
</p>
<div class="contact-hr"></div>
<form class="layui-form" id="Form1" lay-filter="Form1" autocomplete="off" style="display:block;">
<div class="layui-row layui-col-space20">
<div class="layui-col-xs12 layui-col-md12">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.full_name')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<input type="text" name="name" value=""
placeholder="{:lang('contact.full_name')}"
lay-verify="required" class="layui-input">
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.email')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<input type="text" name="email" value=""
placeholder="{:lang('contact.email')}"
lay-verify="required|email" class="layui-input">
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.repeat_email')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<input type="text" name="re_email" value=""
placeholder="{:lang('contact.repeat_email')}"
lay-verify="required|email|sameEmail" class="layui-input">
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.passport_number')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<input type="text" name="passport_number" value=""
placeholder="{:lang('contact.passport_number')}"
lay-verify="required" class="layui-input">
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.is_apply')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group" style="gap:22px;">
<input type="radio" name="is_apply" value="1" title="{:lang('contact.yes')}" alt="is_apply_1" lay-verify="otherReq"/>
<input type="radio" name="is_apply" value="0" title="{:lang('contact.no')}" alt="is_apply_0" lay-verify="otherReq"/>
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-row data-name-is_apply data-is_apply_1">
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.contact_reason')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<select name="reason_contact" class="layui-required">
<option value="">{:lang('contact.contact_reason')}</option>
{foreach $reason as $key=>$value}
<option value="{$value}">{$value}</option>
{/foreach}
</select>
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.order_sn')}<span style="color:#999;font-weight:600;">{:lang('contact.optional')}</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<input type="text" name="order_sn" value=""
placeholder="{:lang('contact.order_sn')}"
class="layui-input">
</div>
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md12">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('contact.comment')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<textarea name="comments" class="layui-textarea" placeholder="{:lang('contact.comment')}" lay-verify="required"></textarea>
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md12">
<div class="layui-form-item captcha-item">
<label class="layui-form-label">{:lang('contact.yzm')}<span>*</span></label>
<div class="layui-input-block">
<div class="layui-input-group">
<input type="text" name="yzm" value=""
placeholder="{:lang('contact.yzm')}"
lay-verify="required" class="layui-input">
<div class="layui-input-group-prepend">
<div class="captcha-img-container">
<img src="{:captcha_src()}" id="captcha-img"
onclick="refreshCaptcha()"
style="cursor:pointer; border:1px solid #ddd;">
<a href="javascript:void(0);" onclick="refreshCaptcha()">
{:lang('contact.change')}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-col-xs12 text-center contact-actions">
<div class="layui-form-item">
<button class="layui-btn" lay-submit lay-filter="formSubmit" id="submit">
{:lang('contact.submit_now')}
</button>
</div>
</div>
</div>
</form>
</div>
</div>
{include file="public/newfooter" /}
<script>
var layer = layui.layer, form = layui.form, laydate = layui.laydate, util = layui.util;
function refreshCaptcha() {
$('#captcha-img').attr('src', '{:captcha_src()}?t=' + Date.now());
}
form.on('submit(formSubmit)', function (data) {
var submitVal = "";
for (var i = 1; i < 2; i++) {
submitVal = $.extend(submitVal, form.val('Form' + i));
if (!form.validate('#Form' + i)) {
return false;
}
}
var load = layer.load(2, {shade: 0.3})
$.ajax({
url: "/home/contact/do_apply",
type: "POST",
data: submitVal,
success: function (data) {
layer.close(load);
refreshCaptcha()
if (data.code === 1) {
layer.msg(data.msg, {icon: 1}, function () {
window.location.reload()
});
} else {
layer.msg(data.msg, {icon: 5});
return false
}
}
});
return false;
});
form.verify({
/* 确认邮箱一致 */
sameEmail: function(value){
var email = $('input[name="email"]').val() || '';
if ((email.trim() !== (value || '').trim())) {
return '{:lang("contact.repeat_email")} 不一致';
}
},
otherReq: function (value, item) {
var $ = layui.$;
var verifyName = $(item).attr('name'),
verifyType = $(item).attr('type'),
formElem = $(item).parents('.layui-form'),
verifyElem = formElem.find('input[name=' + verifyName + ']'),
isTrue = verifyElem.is(':checked'),
focusElem = verifyElem.next().find('i.layui-icon'),
reqText = $(item).attr('lay-reqtext') || '{:lang("contact.default_reqtext")}';
if (!isTrue || !value) {
focusElem.css(verifyType == 'radio' ? {"color": "#FF5722"} : {"border-color": "#FF5722"});
focusElem.first().attr("tabIndex", "1").css("outline", "0").blur(function () {
focusElem.css(verifyType == 'radio' ? {"color": ""} : {"border-color": ""});
}).focus();
return reqText;
}
}
});
$("input[type='radio']").each(function () {
var alt = $(this).attr("alt");
if ($(this).is(":checked")) {
$(".data-" + alt).show();
} else {
$(".data-" + alt).hide();
}
})
form.on("radio", function (data) {
var name = data.elem.name;
var alt = data.elem.alt;
var $sections = $(".data-name-" + name);
$sections.each(function () {
var $section = $(this);
var isMatch = $section.hasClass("data-" + alt);
if (isMatch) {
$section.show();
} else {
$section.hide();
// 隐藏时移除 required 相关验证(你原逻辑保留)
$section.find("input, textarea, select").each(function () {
var $el = $(this);
var old = $el.attr("lay-verify") || "";
var newVerify = old
.split("|")
.filter(v => !["required", "otherReq", "checkboxChecked"].includes(v))
.join("|");
$el.attr("lay-verify", newVerify);
});
$section.find(".xm-select-default").each(function () {
var $el = $(this);
var old = $el.attr("lay-verify") || "";
var newVerify = old
.split("|")
.filter(v => !["required", "otherReq", "checkboxChecked"].includes(v))
.join("|");
$el.attr("lay-verify", newVerify);
});
}
});
// 只给当前显示块补 required
var $target = $(".data-" + alt);
$target.children().find(".layui-required").each(function () {
const $el = $(this);
const isVisible = $el.closest(".layui-form-item, .layui-col-xs12, .layui-col-md6").is(":visible");
if (!isVisible) return;
let verifyList = ($el.attr("lay-verify") || "").split("|").filter(Boolean);
if ($el.is(":radio")) {
if (!verifyList.includes("otherReq")) verifyList.push("otherReq");
} else if ($el.is(":checkbox")) {
if (!verifyList.includes("checkboxChecked")) verifyList.push("checkboxChecked");
} else {
if (!verifyList.includes("required")) verifyList.push("required");
}
$el.attr("lay-verify", verifyList.join("|"));
});
$target.find(".xm-select-default:visible").each(function () {
var $el = $(this);
var verifyList = ($el.attr("lay-verify") || "").split("|").filter(Boolean);
if (!verifyList.includes("required")) verifyList.push("required");
$el.attr("lay-verify", verifyList.join("|"));
});
form.render("radio");
form.render("checkbox");
});
</script>
</body>

View File

@@ -0,0 +1,279 @@
{include file="public/head" /}
<link rel="stylesheet" href="/static/css/index.css">
<style>
body {
background: #fff !important;
}
</style>
<body>
{include file="public/newnav" /}
<!-- ===== Hero ===== -->
<section class="hero">
<div class="container">
<div class="hero-card">
<h1>{$country|default=''}{:lang('index.visa_service')}</h1>
<p>
{:str_replace('%country%', $country ?? '', lang('index.welcome_message'))} </p>
<a class="btn btn-primary btn-primary-brand w-100" href="/home/visa/index.html">{:lang('index.online_application')}</a>
</div>
</div>
</section>
<!-- ===== Notice ===== -->
<section class="notice">
<div class="container">
<p>
<span class="tip-title">{:lang('index.notice')}</span>
{:lang('index.notice_text')}
</p>
</div>
</section>
<!-- ===== Flow ===== -->
<section class="section">
<div class="container">
<div class="section-title">{:lang('index.process_title')}</div>
<div class="row g-3 align-items-stretch">
<div class="col-lg-7">
<div class="flow2">
<div class="step">
<div class="badge-num">1</div>
<div>
<h6>{:lang('index.step1_title')}</h6>
<p>{:lang('index.step1_desc')}</p>
</div>
</div>
<div class="step">
<div class="badge-num">2</div>
<div>
<h6>{:lang('index.step2_title')}</h6>
<p>{:lang('index.step2_desc')}</p>
</div>
</div>
<div class="step">
<div class="badge-num">3</div>
<div>
<h6>{:lang('index.step3_title')}</h6>
<p>{:lang('index.step3_desc')}</p>
</div>
</div>
</div>
</div>
<div class="col-lg-5">
<div class="flow2-side">
<div class="k"><i class="bi bi-lightning-charge me-1"></i>{:lang('index.tip')}</div>
<div class="i">
• {:lang('index.tip_1')}<br>
• {:lang('index.tip_2')}<br>
• {:lang('index.tip_3')}<br>
• {:lang('index.tip_4')}
</div>
<div class="d-grid mt-3">
<a class="btn btn-primary btn-primary-brand" href="/home/visa/index.html">{:lang('index.online_application')}</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ===== Intro + Map ===== -->
<section class="section pt-0">
<div class="container">
<div class="intro2" id="apply">
<div class="row g-0">
<div class="col-lg-5">
<div class="left"></div>
</div>
<div class="col-lg-7">
<div class="right">
<h2>{$country|default=''}{:lang('index.entry_card_application')}</h2>
<p style="color: rgba(15,23,42,.72); line-height:1.9; margin:0;">
{:str_replace('%country%', $country ?? '', lang('index.application_description1'))}
</p>
<div class="bul">
<div><i class="bi bi-check2-circle"></i> {:str_replace('%country%', $country ?? '', lang('index.application_notice'))}</div>
</div>
<a class="btn btn-outline-brand w-100" href="/home/visa/index.html">
{$country|default=''}{:lang('index.apply_now')}
</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ===== 为什么选择我们 ===== -->
<section class="section pt-0">
<div class="container">
<div class="why-title"> {:lang('index.why_choose_us_title')}</div>
<div class="why-sub"> {:lang('index.why_choose_us_subtitle')}</div>
<div class="row g-3">
<div class="col-lg-6">
<div class="why2-item">
<div class="why2-ico"><i class="bi bi-lightning-charge"></i></div>
<div>
<h6>{:lang('index.convenient')}</h6>
<p>{:lang('index.convenient_desc')}</p>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="why2-item">
<div class="why2-ico"><i class="bi bi-shield-check"></i></div>
<div>
<h6>{:lang('index.secure')}</h6>
<p>{:lang('index.secure_desc')}</p>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="why2-item">
<div class="why2-ico"><i class="bi bi-award"></i></div>
<div>
<h6>{:lang('index.approval')}</h6>
<p>{:lang('index.approval_desc')}</p>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="why2-item">
<div class="why2-ico"><i class="bi bi-headset"></i></div>
<div>
<h6>{:lang('index.support')}</h6>
<p>{:lang('index.support_desc')}</p>
</div>
</div>
</div>
</div>
<div class="text-center mt-4">
<a class="btn btn-primary btn-primary-brand px-5" href="/home/visa/index.html">
{$country|default=''}{:lang('index.apply_now')}
</a>
</div>
</div>
</section>
<section class="section pt-0" id="plans">
<div class="container">
<div class="section-title">{:lang('index.setmeal')}</div>
<div class="row g-3">
<div class="col-lg-4">
<div class="plan">
<span class="tag">{:lang('index.setmeal_st')}</span>
<h5>{:lang('index.suitable_for_advance_preparation')}</h5>
<div class="meta">{:lang('index.processing_time_limit')}</div>
<ul>
<li>{:lang('index.chinese_guided_filling')}</li>
<li>{:lang('index.key_field_consistency_check')}</li>
<li>{:lang('index.basic_error_reminder')}</li>
</ul>
<div class="d-grid mt-3">
<a class="btn btn-outline-brand" href="/home/visa/index.html">{:lang('index.select_standard_plan')}</a>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="plan" style="border-color: rgba(240,90,40,.35);">
<span class="tag" style="border-color: rgba(240,90,40,.22); background: rgba(240,90,40,.10);">{:lang('index.setmeal_ex')}</span>
<h5>{:lang('index.suitable_for_departure_soon')}</h5>
<div class="meta">{:lang('index.urgent_availability')}</div>
<ul>
<li>{:lang('index.priority_processing_queue')}</li>
<li>{:lang('index.stricter_reminder_for_key_fields')}</li>
<li>{:lang('index.faster_feedback_and_supplementary_suggestions')}</li>
</ul>
<div class="d-grid mt-3">
<a class="btn btn-accent" href="/home/visa/index.html">{:lang('index.select_urgent_plan')}</a>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="plan">
<span class="tag">{:lang('index.setmeal_team')}</span>
<h5>{:lang('index.group_travel_batch_reg')}</h5>
<div class="meta">{:lang('index.group_meta_info')}</div>
<ul>
<li>{:lang('index.batch_data_collect')}</li>
<li>{:lang('index.unified_verify_submit')}</li>
<li>{:lang('index.progress_track_reconcile')}</li>
</ul>
<div class="d-grid mt-3">
<a class="btn btn-primary btn-primary-brand" href="/home/contact">{:lang('index.contact_team_plan')}</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ===== 新增模块 2FAQ提升转化不改整体风格 ===== -->
<section class="section pt-0" id="faq">
<div class="container">
<div class="section-title">{:lang('index.common_questions')}</div>
<div class="accordion" id="accFaq">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#f1">
{:lang('index.faq_is_official_website')}
</button>
</h2>
<div id="f1" class="accordion-collapse collapse" data-bs-parent="#accFaq">
<div class="accordion-body" style="color: rgba(15,23,42,.72); line-height: 1.9; font-size: 13px;">
{:lang('index.faq_is_official_website_answer')}
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#f2">
{:lang('index.faq_guarantee_approval')}
</button>
</h2>
<div id="f2" class="accordion-collapse collapse" data-bs-parent="#accFaq">
<div class="accordion-body" style="color: rgba(15,23,42,.72); line-height: 1.9; font-size: 13px;">
{:lang('index.faq_guarantee_approval_answer')}
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#f3">
{:lang('index.faq_query_download_after_payment')}
</button>
</h2>
<div id="f3" class="accordion-collapse collapse" data-bs-parent="#accFaq">
<div class="accordion-body" style="color: rgba(15,23,42,.72); line-height: 1.9; font-size: 13px;">
{:lang('index.faq_query_download_after_payment_answer')}
</div>
</div>
</div>
</div>
</div>
</section>
{include file="public/newfooter" /}
</body>
</html>

View File

@@ -0,0 +1,279 @@
{include file="public/head" /}
<link rel="stylesheet" href="/static/css/index.css">
<style>
body {
background: #fff !important;
}
</style>
<body>
{include file="public/newnav" /}
<!-- ===== Hero ===== -->
<section class="hero">
<div class="container">
<div class="hero-card">
<h1>{$country|default=''}{:lang('index.visa_service')}</h1>
<p>
{:str_replace('%country%', $country ?? '', lang('index.welcome_message'))} </p>
<a class="btn btn-primary btn-primary-brand w-100" href="/home/visa/index.html">{:lang('index.online_application')}</a>
</div>
</div>
</section>
<!-- ===== Notice ===== -->
<section class="notice">
<div class="container">
<p>
<span class="tip-title">{:lang('index.notice')}</span>
{:lang('index.notice_text')}
</p>
</div>
</section>
<!-- ===== Flow ===== -->
<section class="section">
<div class="container">
<div class="section-title">{:lang('index.process_title')}</div>
<div class="row g-3 align-items-stretch">
<div class="col-lg-7">
<div class="flow2">
<div class="step">
<div class="badge-num">1</div>
<div>
<h6>{:lang('index.step1_title')}</h6>
<p>{:lang('index.step1_desc')}</p>
</div>
</div>
<div class="step">
<div class="badge-num">2</div>
<div>
<h6>{:lang('index.step2_title')}</h6>
<p>{:lang('index.step2_desc')}</p>
</div>
</div>
<div class="step">
<div class="badge-num">3</div>
<div>
<h6>{:lang('index.step3_title')}</h6>
<p>{:lang('index.step3_desc')}</p>
</div>
</div>
</div>
</div>
<div class="col-lg-5">
<div class="flow2-side">
<div class="k"><i class="bi bi-lightning-charge me-1"></i>{:lang('index.tip')}</div>
<div class="i">
• {:lang('index.tip_1')}<br>
• {:lang('index.tip_2')}<br>
• {:lang('index.tip_3')}<br>
• {:lang('index.tip_4')}
</div>
<div class="d-grid mt-3">
<a class="btn btn-primary btn-primary-brand" href="/home/visa/index.html">{:lang('index.online_application')}</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ===== Intro + Map ===== -->
<section class="section pt-0">
<div class="container">
<div class="intro2" id="apply">
<div class="row g-0">
<div class="col-lg-5">
<div class="left"></div>
</div>
<div class="col-lg-7">
<div class="right">
<h2>{$country|default=''}{:lang('index.entry_card_application')}</h2>
<p style="color: rgba(15,23,42,.72); line-height:1.9; margin:0;">
{:str_replace('%country%', $country ?? '', lang('index.application_description1'))}
</p>
<div class="bul">
<div><i class="bi bi-check2-circle"></i> {:str_replace('%country%', $country ?? '', lang('index.application_notice'))}</div>
</div>
<a class="btn btn-outline-brand w-100" href="/home/visa/index.html">
{$country|default=''}{:lang('index.apply_now')}
</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ===== 为什么选择我们 ===== -->
<section class="section pt-0">
<div class="container">
<div class="why-title"> {:lang('index.why_choose_us_title')}</div>
<div class="why-sub"> {:lang('index.why_choose_us_subtitle')}</div>
<div class="row g-3">
<div class="col-lg-6">
<div class="why2-item">
<div class="why2-ico"><i class="bi bi-lightning-charge"></i></div>
<div>
<h6>{:lang('index.convenient')}</h6>
<p>{:lang('index.convenient_desc')}</p>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="why2-item">
<div class="why2-ico"><i class="bi bi-shield-check"></i></div>
<div>
<h6>{:lang('index.secure')}</h6>
<p>{:lang('index.secure_desc')}</p>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="why2-item">
<div class="why2-ico"><i class="bi bi-award"></i></div>
<div>
<h6>{:lang('index.approval')}</h6>
<p>{:lang('index.approval_desc')}</p>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="why2-item">
<div class="why2-ico"><i class="bi bi-headset"></i></div>
<div>
<h6>{:lang('index.support')}</h6>
<p>{:lang('index.support_desc')}</p>
</div>
</div>
</div>
</div>
<div class="text-center mt-4">
<a class="btn btn-primary btn-primary-brand px-5" href="/home/visa/index.html">
{$country|default=''}{:lang('index.apply_now')}
</a>
</div>
</div>
</section>
<section class="section pt-0" id="plans">
<div class="container">
<div class="section-title">{:lang('index.setmeal')}</div>
<div class="row g-3">
<div class="col-lg-4">
<div class="plan">
<span class="tag">{:lang('index.setmeal_st')}</span>
<h5>{:lang('index.suitable_for_advance_preparation')}</h5>
<div class="meta">{:lang('index.processing_time_limit')}</div>
<ul>
<li>{:lang('index.chinese_guided_filling')}</li>
<li>{:lang('index.key_field_consistency_check')}</li>
<li>{:lang('index.basic_error_reminder')}</li>
</ul>
<div class="d-grid mt-3">
<a class="btn btn-outline-brand" href="/home/visa/index.html">{:lang('index.select_standard_plan')}</a>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="plan" style="border-color: rgba(240,90,40,.35);">
<span class="tag" style="border-color: rgba(240,90,40,.22); background: rgba(240,90,40,.10);">{:lang('index.setmeal_ex')}</span>
<h5>{:lang('index.suitable_for_departure_soon')}</h5>
<div class="meta">{:lang('index.urgent_availability')}</div>
<ul>
<li>{:lang('index.priority_processing_queue')}</li>
<li>{:lang('index.stricter_reminder_for_key_fields')}</li>
<li>{:lang('index.faster_feedback_and_supplementary_suggestions')}</li>
</ul>
<div class="d-grid mt-3">
<a class="btn btn-accent" href="/home/visa/index.html">{:lang('index.select_urgent_plan')}</a>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="plan">
<span class="tag">{:lang('index.setmeal_team')}</span>
<h5>{:lang('index.group_travel_batch_reg')}</h5>
<div class="meta">{:lang('index.group_meta_info')}</div>
<ul>
<li>{:lang('index.batch_data_collect')}</li>
<li>{:lang('index.unified_verify_submit')}</li>
<li>{:lang('index.progress_track_reconcile')}</li>
</ul>
<div class="d-grid mt-3">
<a class="btn btn-primary btn-primary-brand" href="/home/contact">{:lang('index.contact_team_plan')}</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ===== 新增模块 2FAQ提升转化不改整体风格 ===== -->
<section class="section pt-0" id="faq">
<div class="container">
<div class="section-title">{:lang('index.common_questions')}</div>
<div class="accordion" id="accFaq">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#f1">
{:lang('index.faq_is_official_website')}
</button>
</h2>
<div id="f1" class="accordion-collapse collapse" data-bs-parent="#accFaq">
<div class="accordion-body" style="color: rgba(15,23,42,.72); line-height: 1.9; font-size: 13px;">
{:lang('index.faq_is_official_website_answer')}
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#f2">
{:lang('index.faq_guarantee_approval')}
</button>
</h2>
<div id="f2" class="accordion-collapse collapse" data-bs-parent="#accFaq">
<div class="accordion-body" style="color: rgba(15,23,42,.72); line-height: 1.9; font-size: 13px;">
{:lang('index.faq_guarantee_approval_answer')}
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#f3">
{:lang('index.faq_query_download_after_payment')}
</button>
</h2>
<div id="f3" class="accordion-collapse collapse" data-bs-parent="#accFaq">
<div class="accordion-body" style="color: rgba(15,23,42,.72); line-height: 1.9; font-size: 13px;">
{:lang('index.faq_query_download_after_payment_answer')}
</div>
</div>
</div>
</div>
</div>
</section>
{include file="public/newfooter" /}
</body>
</html>

View File

@@ -0,0 +1,182 @@
{include file="public/head" /}
<link rel="stylesheet" href="/static/css/user.css?v={:time()}">
<link rel="stylesheet" href="/static/css/invoice.css?v={:time()}">
</head>
<body class="user-page">
{include file="public/newnav" /}
<main class="user-main">
<div class="layui-container">
<div class="account-layout">
<aside class="account-nav">
<div class="account-nav__title">个人中心</div>
<a href="{:url('user/index')}" class="account-nav__link">
<i class="bi bi-receipt"></i><span>我的订单</span>
</a>
<a href="{:url('user/invoice')}" class="account-nav__link is-active">
<i class="bi bi-file-earmark-text"></i><span>自助开票</span>
</a>
<a href="{:url('user/multiple')}" class="account-nav__link">
<i class="bi bi-files"></i><span>合并开票</span>
</a>
</aside>
<section class="account-panel">
<div class="account-panel__title">发票申请</div>
<div class="account-panel__body">
<div class="invoice-summary">
<div>
<strong>开票金额</strong>
<span>{$data.total_price}</span>
</div>
<p>提交后,商家将在 1-3 个工作日内完成开票。</p>
</div>
<p class="invoice-tip">
个人开票时,税号请填写身份证号码;企业开票请填写统一社会信用代码。
</p>
<form class="layui-form invoice-form" action="">
<div class="invoice-section-title">必填信息</div>
<div class="layui-row layui-col-space24">
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">手机号<span>*</span></label>
<div class="layui-input-block">
<input type="tel" autocomplete="off" class="layui-input" lay-verify="required|phone" name="phone" placeholder="用于接收发票通知">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">客户名称<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="title" lay-verify="required" placeholder="限55个字符" maxlength="55" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">税号<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="tax_number" lay-verify="required" placeholder="请输入税号或身份证号码" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">联系人<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="link_person" lay-verify="required" placeholder="请输入联系人姓名" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12">
<div class="layui-form-item">
<label class="layui-form-label">邮箱<span>*</span></label>
<div class="layui-input-block">
<input type="email" name="link_email" lay-verify="required|email" placeholder="用于接收电子发票" autocomplete="off" class="layui-input">
</div>
</div>
</div>
</div>
<div class="invoice-section-title invoice-section-title--sub">补充信息</div>
<div class="layui-row layui-col-space24">
<div class="layui-col-xs12">
<div class="layui-form-item">
<label class="layui-form-label">地址</label>
<div class="layui-input-block">
<input type="text" name="address" placeholder="请输入注册地址" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">电话</label>
<div class="layui-input-block">
<input type="text" name="mobile" placeholder="请输入联系电话" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">开户行</label>
<div class="layui-input-block">
<input type="text" name="bank" placeholder="请输入开户银行" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12">
<div class="layui-form-item">
<label class="layui-form-label">账号</label>
<div class="layui-input-block">
<input type="text" name="account" placeholder="请输入银行账号" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12">
<input type="hidden" name="order_id" value="{$data.id}">
<input type="hidden" name="order_sn" value="{$data.order_sn}">
<button type="submit" id="button" class="layui-btn layui-btn-fluid" lay-submit lay-filter="demo1">
<span>提交申请</span>
</button>
</div>
</div>
</form>
</div>
</section>
</div>
</div>
</main>
{include file="public/newfooter" /}
<!--//倒计时-->
<script>
layui.use(function(){
var $ = layui.$;
var form = layui.form;
var layer = layui.layer;
// 提交事件
form.on('submit(demo1)', function(data){
var field = data.field; // 获取表单字段值
field.tax_number = $.trim(field.tax_number || '');
var tax_len = field.tax_number.length;
var arr = [15, 17, 18, 20];
if (arr.indexOf(tax_len) == -1) {
layer.msg('仅支持购方税号长度为15、17、18、20请重新输入');
return false;
}
var $button = $('#button');
var buttonText = $button.find('span').text();
$button.prop('disabled', true).find('span').text('提交中...');
$.ajax({
url: "/home/invoice/add_invoice",
type: 'POST',
data: field,
success: function (res) {
if (res.status == 1) {
layer.alert('已提交开票申请,商家会在 1-3个工作日内完成开票,<br>请扫描下方二维码进行添加客服收取发票<br><div class="in-img"><img src="{$fapiao_kefu}" width: 150px;></div>', {
icon: 1,
title:'发票申请',
closeBtn: 0
}, function(index){
layer.close(index);
window.location.href = res.url;
});
} else {
$button.prop('disabled', false).find('span').text(buttonText);
layer.msg(res.msg || '提交失败,请稍后重试');
}
},
error: function () {
$button.prop('disabled', false).find('span').text(buttonText);
layer.msg('网络异常,请稍后重试');
}
})
return false; // 阻止默认 form 跳转
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,589 @@
{include file="public/head"}
<style>
/* ---- Card ---- */
.lookup-card{
background:#fff;
border:1px solid rgba(0,0,0,.06);
box-shadow: 0 2px 10px rgba(0,0,0,.06);
border-radius: 6px;
margin: 18px 0 26px;
overflow:hidden;
max-width: 100%;
}
.lookup-bar{
background: var(--main-color);
color:#fff;
font-size: 24px;
font-weight: 900;
padding: 8px 16px;
line-height: 1.2;
}
.lookup-body{
padding: 18px 22px 8px;
}
/* ---- Form labels above ---- */
.label-above .layui-form-label{
display:block;
float:none;
width:auto !important;
text-align:left;
padding: 0 0 6px 0;
font-weight: 800;
font-size: 18px; /* PC 默认就用 18避免过大 */
color:#3a3a3a;
}
.label-above .layui-form-label span{
color:#e53935;
margin-left:4px;
font-weight:900;
}
.layui-input-block{
margin-left:0;
min-height: auto;
box-sizing: border-box;
}
/* ---- Inputs ---- */
.layui-form input.layui-input{
height: 50px; /* 统一高度 */
border: 1px solid #d6d6d6;
border-radius: 2px;
padding: 10px 12px;
font-size: 14px; /* PC 默认 14 */
color:#333;
background:#fff;
box-sizing: border-box;
}
/* Layui date input background keep */
.layui-input-wrap .layui-input-prefix + .layui-input,
.layui-input-wrap .layui-input-prefix ~ * .layui-input{
background:#fff;
}
/* ---- Date prefix icon vertical align ---- */
.layui-input-wrap{
display: flex;
align-items: center; /* 关键prefix 与 input 垂直居中 */
}
.layui-input-wrap .layui-input-prefix{
display: flex;
align-items: center;
justify-content: center;
height: 50px;
line-height: 50px;
}
/* ---- Help tip ---- */
.help-tip{
margin-top: 8px;
display:flex;
align-items:flex-start;
gap: 6px;
color:#666;
font-size: 13px;
line-height: 1.6;
}
.help-tip i{
font-size: 14px;
color:#333;
margin-top: 2px;
}
/* ---- Button ---- */
.btn-row{ margin-top: 8px; }
.layui-btn{
background:#153854;
border:1px solid #153854;
box-shadow: 0 2px 4px rgba(0,0,0,.35);
border-radius: 6px;
font-size: 20px;
font-weight: 900;
height: 50px;
line-height: 50px;
padding: 0 28px;
letter-spacing: .5px;
box-sizing: border-box;
}
.btn-fixed{
width: 520px;
max-width: 100%;
}
/* ---- Result area ---- */
#result{ padding: 0 22px 22px; }
.container-tit{
font-size: 18px;
font-weight: 900;
color:#153854;
margin: 12px 0 8px;
}
.container-con{
font-size: 14px;
color:#555;
margin-bottom: 12px;
}
.layui-table thead{
color:#fff;
background:#153854;
}
.layui-laydate .layui-this,
.layui-laydate .layui-this > div{
background-color:#153854 !important;
color:#fff !important;
}
/* ---- Prevent overflow caused by row negative margin / fixed width elements ---- */
.layui-row{
max-width: 100%;
box-sizing: border-box;
}
img, table, .layui-table-view{
max-width: 100% !important;
}
/* ===============================
Mobile adjustments
=============================== */
@media (max-width: 768px){
/* 容器左右留白,防止撑破 */
.layui-container{
padding-left: 12px !important;
padding-right: 12px !important;
}
/* row 可能有负 margin清掉 */
.layui-row{
margin-left: 0 !important;
margin-right: 0 !important;
}
/* 卡片内部 padding 更紧凑 */
.lookup-bar{
font-size: 22px;
padding: 12px 14px;
}
.lookup-body{
padding: 14px 14px 6px;
}
.label-above .layui-form-label{
font-size: 18px;
}
.layui-form input.layui-input{
height: 50px;
font-size: 15px;
}
.layui-input-wrap .layui-input-prefix{
height: 50px;
line-height: 50px;
}
.layui-btn{
font-size: 22px;
height: 62px;
line-height: 62px;
width: 100%;
}
.layui-table-header{ display:none; }
}
/* ===============================
Desktop fine-tune (optional)
=============================== */
@media (min-width: 992px){
/* 大屏让卡片更像截图:稍微增加左右 padding */
.lookup-body{ padding: 18px 22px 8px; }
}
</style>
<body>
<div class="zh_content">
{include file="public/newnav" }
</div>
<div class="layui-container">
<div class="lookup-card">
<div class="lookup-bar">{:lang('lookup.status_query_title')}</div>
<div class="lookup-body">
<form class="layui-form label-above" lay-filter="Form1" id="Form1" autocomplete="off">
<div class="layui-row layui-col-space30" style="margin-top: 10px;">
<!-- 护照号 -->
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="passport_number">
{:lang('lookup.passport_number')}<span>*</span>
</label>
<div class="layui-input-block passport-wrap">
<input type="text" name="passport_number" lay-verify="required"
placeholder="{:lang('lookup.passport_number_placeholder')}"
class="layui-input" value="">
<!-- 关键:给 file 一个 id并隐藏 -->
<input id="passport_file" class="passport-file" type="file" accept="image/*"
onchange="uploadPassport(this,'passport')">
<!-- 关键label 的 for 指向 file 的 id -->
<label class="passport-upload" for="passport_file" title="拍照/上传有效护照照片">
<i class="layui-icon layui-icon-camera"></i>
</label>
</div>
</div>
</div>
<!-- 出生日期 -->
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="birth_date">
{:lang('lookup.birth_date')}<span>*</span>
</label>
<div class="layui-input-block">
<div class="layui-input-wrap">
<div class="layui-input-prefix">
<i class="layui-icon layui-icon-date"></i>
</div>
<input type="text" name="birth_date" id="birth_date" lay-verify="required"
placeholder="{:lang('lookup.birth_date_placeholder')}"
class="layui-input layui-time" value="">
</div>
</div>
</div>
</div>
<!---->
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('lookup.last_name')}<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="last_name" lay-verify="required|english"
placeholder="{:lang('lookup.last_name_placeholder')}"
class="layui-input" value="">
<div class="help-tip">
<i class="layui-icon layui-icon-tips-fill"></i>
<span>{:lang('lookup.last_name_tooltip')}</span>
</div>
</div>
</div>
</div>
<!---->
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">{:lang('lookup.first_name')}<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="first_name" lay-verify="required|english"
placeholder="{:lang('lookup.first_name_placeholder')}"
class="layui-input" value="">
<div class="help-tip">
<i class="layui-icon layui-icon-tips-fill"></i>
<span>{:lang('lookup.first_name_tooltip')}</span>
</div>
</div>
</div>
</div>
<!-- 按钮:半宽且不铺满 -->
<div class="layui-col-xs12 layui-col-md6 btn-row">
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn btn-fixed" lay-submit lay-filter="formSubmit" id="submit">
{:lang('lookup.query_status_button')}
</button>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="layui-row container" style="display:none" id="result">
<div class="layui-col-xs12 layui-col-md12" style="display:none" id="result1">
<div class="container-tit">{:lang('lookup.registered_status')}</div>
<div class="container-con">{:lang('lookup.approval_message')}</div>
</div>
<div class="layui-col-xs12 layui-col-md12">
<table class="layui-hide" id="demo"></table>
</div>
</div>
</div>
</div>
{include file="public/newfooter" }
<script>
var layer = layui.layer, form = layui.form, laydate = layui.laydate, table = layui.table;
var ocrConfigRaw = '{$ocr_config|default = ''}'.replace(/&quot;/g, '"');
var ocrConfig = {};
var sdk = null;
try {
ocrConfig = ocrConfigRaw ? JSON.parse(ocrConfigRaw) : {};
} catch (e) {
console.error('Failed to parse OCR config', e);
}
if (window.PassportOCRSDK && ocrConfig.base_url && ocrConfig.app_id && ocrConfig.signature && ocrConfig.timestamp) {
try {
sdk = PassportOCRSDK.create({
apiBase: ocrConfig.base_url,
appId: ocrConfig.app_id,
sign: ocrConfig
});
} catch (e) {
console.error('Failed to init OCR sdk', e);
}
}
// 更稳的手机判断
function isMobile() {
return window.matchMedia && window.matchMedia('(max-width: 767.98px)').matches;
}
// 日期控件(建议固定格式)
$(".layui-form-item").find(".layui-time").each(function () {
laydate.render({
elem: this,
trigger: 'click',
format: 'yyyy-MM-dd'
});
});
// ✅兼容你 onchange 传了第二个参数
function uploadPassport(_this, type) {
var file = _this.files && _this.files[0];
var loadingIndex = null;
var closeLoading = function () {
if (loadingIndex !== null) {
layer.close(loadingIndex);
loadingIndex = null;
}
};
var resetFileInput = function () {
$(_this).val('');
};
if (!file) {
layer.msg('{:lang("controller.upload_missing")}', {icon: 5});
return false;
}
if (!/image\/\w+/.test(file.type || '')) {
layer.msg("{:lang('js.upload_image_only')}", {icon: 5});
return false;
}
if (!FileReader) {
layer.msg("{:lang('js.browser_not_supported')}", {icon: 5});
return false;
}
var url = "/home/upload/upload_passport";
loadingIndex = layer.load(2, {shade: 0.3});
compressImage(file, 0.2, async function (compressedFile) {
try {
if (!compressedFile) {
closeLoading();
resetFileInput();
layer.msg('{:lang("js.upload_retry")}', {icon: 5});
return false;
}
var ocrWarningMsg = '';
if (sdk) {
try {
var ocrData = await sdk.recognizePassport(compressedFile, {
auto_rotate: true,
return_debug: false
});
if (ocrData && ocrData.code === 1 && ocrData.data) {
var ocr = ocrData.data || {};
var pad2 = function (n) {
return String(n || '').padStart(2, '0');
};
var buildDate = function (y, m, d) {
if (!y || !m || !d) return '';
return y + '-' + pad2(m) + '-' + pad2(d);
};
$('input[name="last_name"]').val(ocr.last_name || '');
$('input[name="first_name"]').val(ocr.first_name || '');
$('input[name="passport_number"]').val(ocr.passport_number || '');
var birthDate = buildDate(ocr.birth_date_year, ocr.birth_date_month, ocr.birth_date_day);
if (birthDate) {
$('input[name="birth_date"]').val(birthDate);
}
} else {
ocrWarningMsg = '{:lang("js.manual_entry_required")}';
}
} catch (ocrErr) {
ocrWarningMsg = '{:lang("js.manual_entry_required")}';
}
} else {
ocrWarningMsg = '{:lang("js.manual_entry_required")}';
}
var formData = new FormData();
formData.append('file', compressedFile);
formData.append('path', 'thailand');
$.ajax({
url: url,
type: "post",
data: formData,
dataType: "json",
contentType: false,
processData: false,
mimeType: "multipart/form-data",
success: function (res) {
closeLoading();
resetFileInput();
if (res.code != 1) {
//layer.msg('{:lang("js.manual_entry_required")}', {icon: 5});
//return;
}
},
error: function () {
closeLoading();
resetFileInput();
//layer.msg('{:lang("js.manual_entry_required")}', {icon: 5});
}
});
if (ocrWarningMsg) {
layer.msg(ocrWarningMsg, {icon: 5});
}
} catch (e) {
closeLoading();
resetFileInput();
layer.msg('{:lang("js.manual_entry_required")}', {icon: 5});
}
});
}
function compressImage(file, quality, callback) {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function (event) {
const img = new Image();
img.src = event.target.result;
img.onload = function () {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let maxWidth = 1200;
let width = img.width;
let height = img.height;
if (width > maxWidth) {
height *= maxWidth / width;
width = maxWidth;
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(function (blob) {
const newFile = new File([blob], file.name, { type: file.type });
callback(newFile);
}, file.type, quality);
};
};
}
// 监听提交
form.on('submit(formSubmit)', function (data) {
$('#result').hide();
$('#result1').hide();
$.ajax({
type: "POST",
url: "/home/lookup/enrollment",
data: data.field,
beforeSend: function () {
window.index1 = layer.load(1, {shade: [0.3, '#fff']});
},
success: function (_data) {
layer.close(window.index1);
if (_data.code == 0) {
layer.msg(_data.msg, {icon: 5});
return;
}
$('#result').show();
var row = _data.data || {};
var hasFile = !!(row.file_url && row.file_url !== '');
if (isMobile()) {
// 手机:字段-值
var mobileData = [
{field: '{:lang("lookup.passport_number")}', value: row.passport_number || ''},
{field: '{:lang("lookup.birth_date")}', value: row.birth_date || ''},
{field: '{:lang("lookup.last_name")}', value: row.last_name || ''},
{field: '{:lang("lookup.first_name")}', value: row.first_name || ''},
{field: hasFile ? '{:lang("lookup.document")}' : '{:lang("lookup.status")}', value: row.file_url || ''}
];
if (hasFile) $('#result1').show();
table.render({
elem: '#demo',
data: mobileData,
cols: [[
{field:'field', title:'{:lang("lookup.field")}', width:110},
{
field:'value',
title:'{:lang("lookup.value")}',
templet: function(d){
if (d.field === '{:lang("lookup.document")}') {
return '<a href="'+ row.file_url +'?'+ Math.random() +'" target="_blank"><img src="/static/image/pdf.png" alt="{:lang("lookup.click_download")}"></a>';
}
if (d.field === '{:lang("lookup.status")}') {
return '{:lang("lookup.in_progress")}';
}
return d.value;
}
}
]]
});
} else {
if (hasFile) $('#result1').show();
table.render({
elem: '#demo',
data: [row],
cols: [[
{field:'last_name', title:'{:lang("lookup.last_name")}', width:200},
{field:'first_name', title:'{:lang("lookup.first_name")}', width:200},
{field:'birth_date', title:'{:lang("lookup.birth_date")}', width:200},
{field:'passport_number', title:'{:lang("lookup.passport_number")}'},
{
field:'file_url',
title: hasFile ? '{:lang("lookup.document")}' : '{:lang("lookup.status")}',
templet: function(){
if (hasFile) {
return '<a href="'+ row.file_url +'?'+ Math.random() +'" target="_blank"><img src="/static/image/pdf.png" alt="{:lang("lookup.click_download")}"></a>';
}
return '{:lang("lookup.in_progress")}';
}
}
]]
});
}
}
});
return false;
});
form.verify({
english: [/^[A-Za-z]+$/, '{:lang("lookup.english_only")}']
});
</script>
</body>
</html>

View File

@@ -0,0 +1,141 @@
{include file="public/head"}
</head>
<style>
@media (max-width: 1110px) {
.nav a {
width: 35%;
}
}
</style>
<style>
.news_cc {
width: 100%;
}
a:link, a:visited {
text-decoration: none;
}
.news_item {
width: 100%;
height: auto;
overflow: hidden;
margin-top: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
cursor: pointer;
display: block;
}
.news_intro p,.news_intro span{
width: 100%;
line-height: 24px !important;
font-size: 13px !important;
color: #888 !important;
margin: 0 !important;
}
a {
cursor: pointer;
color: #333;
}
.news_text {
float: left;
width: 100%;
}
span{
font-style:unset !important;
}
.news_ct {
width: 100%;
line-height: 18px;
font-size: 15px;
margin-bottom: 8px;
}
.news_intro {
width: 100%;
line-height: 24px;
font-size: 13px;
color: #888;
overflow: hidden;
}
.news_time {
text-align: right;
color: #888;
line-height: 28px;
font-size: 12px;
}
@media (max-width: 720px){
.content {
width: 100%;
padding: 30px 0px 0px;
background: none;
}
}
.layui-laypage .layui-laypage-curr .layui-laypage-em {
background-color: var(--main-color);
}
</style>
<body style="background: #F8F8F8;display: flex; flex-direction: column; min-height: 100vh;">
<div class="zh_content">
{include file="public/newnav" }
</div>
<div class="layui-container" style="flex: 1;margin-top: 36px;">
<div class="modular-row callout bg-grey">
<div class="container">
<div class="news_cc">
{volist name="news_list" id="vo"}
<a class="news_item" href="{:url('news/detail', ['id' => $vo.id])}">
<div class="news_text">
<div class="news_ct animate_tit">{:cookie('think_var') == 'en-us' ? $vo['en_title'] : $vo['title']}</div>
<div class="news_intro">
<?= mb_strlen($plain = strip_tags(html_entity_decode(cookie('think_var') == 'en-us' ? $vo['en_contents'] : $vo['contents']))) > 100 ? htmlspecialchars(mb_substr($plain, 0, 100, 'UTF-8')).'...' : htmlspecialchars($plain) ?>
</div>
<div class="news_time">{$vo.create_time|date='Y-m-d H:i:s'}</div>
</div>
</a>
{/volist}
</div>
<div style="width: 100%; text-align: center;" id="case_page"></div>
</div>
</div>
</div>
{include file="public/newfooter" }
<script>
layui.use(function(){
var laypage = layui.laypage;
// 自定义每页条数的选择项
laypage.render({
elem: 'case_page',
count: '{$count}',
curr: '{$page}',
limit: '{$size}',
prev: '<em>←</em>',
next: '<em>→</em>',
jump: function(obj, first){
if(!first){
window.location.href = '{:Url("news/index")}?page='+obj.curr;
}
}
});
});
$('a[href*="articles"]').parents(".search-row").addClass("hidden");
$(document).ready(function () {
if (window.location.href.indexOf("query") > -1) {
$(".results").css("visibility", "inherit")
} else {
}
if ($(window).width() > 768) {
$("a:not(a.cookieAccept)").each(function () {
// $(this).attr('target', '_blank');
});
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,69 @@
{include file="public/head"}
<style>
.evus_tit {
width: 100%;
font-size: 24px;
text-align: center;
line-height: 40px;
}
.evus_infor {
color: #666;
font-size: 14px;
text-align: center;
padding: 20px 0;
border-bottom: 1px dashed #ddd;
margin-bottom: 20px;
}
.evus_con {
padding-bottom: 10px;
border-bottom: 1px dashed #ddd;
font-size: 14px;
}
.evus_con img {
margin: 0 auto;
width: 80%;
margin: 10px 0;
}
.evus_con p,
.evus_con span {
font-size: 14px;
}
.evus_other {
margin-top: 10px;
line-height: 30px;
}
@media (max-width: 720px) {
.content {
width: 100%;
padding: 30px 0px 0px;
background: none;
}
}
</style>
<body style="background: #F8F8F8;display: flex; flex-direction: column; min-height: 100vh;">
<div class="zh_content">
{include file="public/newnav" }
</div>
<div class="container" style="flex: 1;">
<div class="modular-row callout bg-grey">
<div class="container">
<h1 class="evus_tit">{:cookie('think_var') == 'en-us' ? $info['en_title'] : $info['title']}</h1>
<div class="evus_infor">
{:lang('index.news_author')}admin {:lang('index.news_time')}{$info.create_time}
</div>
<div class="evus_con">
<?= html_entity_decode(cookie('think_var') == 'en-us' ? $info['en_contents'] : $info['contents']) ?>
</div>
</div>
</div>
</div>
{include file="public/newfooter" }
</body>
</html>

View File

@@ -0,0 +1,47 @@
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>{:lang('pay.wechat_pay_title')}</title>
<script type="text/javascript" src="/static/js/jquery-1.8.3.min.js"></script>
<script type="text/javascript" src="/static/js/jquery-weui.min.js"></script>
<link rel="stylesheet" href="/static/css/weui.min.css">
<link rel="stylesheet" href="/static/css/jquery-weui.min.css">
<script type="text/javascript">
$(document).on("click", "#confirm", function () {
//进行查询订单
$.ajax({
type: "POST",
url: "{:url('pay/h5result',['order_sn'=>$order_sn])}",//+tab,
dataType: "json",
success: function (data) {
$.alert(data.msg);
setTimeout(function () {
location.href = data.url; // 跳转
}, 1000);
}
});
})
$(document).on("click", "#repay", function () {
//进行重新支付
window.location.href="{:url('pay/pay',['order_sn'=>$order_sn])}";
})
</script>
</head>
<body>
<div class="weui-mask weui-mask--visible"></div>
<div class="weui-dialog weui-dialog--visible">
<div class="weui-dialog__hd">
<strong class="weui-dialog__title">{:lang('pay.dialog_title')}</strong>
</div>
<div class="weui-dialog__bd">{:lang('pay.payment_confirmation')}</div>
<div class="weui-dialog__ft">
<a href="javascript:;" class="weui-dialog__btn default" id="repay">{:lang('pay.repay_button')}</a>
<a href="javascript:;" class="weui-dialog__btn primary" id="confirm">{:lang('pay.confirm_button')}</a>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,207 @@
{include file="public/head"}
<link rel="stylesheet" href="/static/css/pcpay.css?v=2">
<style>
.payment-pending {
max-width: 760px;
margin: 0 auto;
text-align: left;
}
.payment-pending-title {
font-size: 24px;
font-weight: 700;
margin-bottom: 16px;
color: #1f2937;
}
.payment-pending-text {
font-size: 16px;
line-height: 1.8;
color: #4b5563;
margin-bottom: 12px;
}
.payment-pending-status {
font-size: 15px;
color: #b45309;
margin-bottom: 24px;
}
.payment-spinner {
display: inline-block;
width: 24px;
height: 24px;
border: 3px solid #e5e7eb;
border-top-color: #2563eb;
border-radius: 50%;
animation: sqpay-spin 0.8s linear infinite;
vertical-align: middle;
margin-right: 10px;
}
@keyframes sqpay-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>
</head>
<body style="display: flex; flex-direction: column; min-height: 100vh;">
<div class="zh_content">
{include file="public/newnav" }
</div>
<div
class="container"
id="payment-success-page"
data-payment-confirmed="{$payment_confirmed}"
data-poll-status-url="{$poll_status_url}"
>
<div class="modular-row callout bg-grey" style="margin-top: 50px; flex: 1;">
<div class="container">
<div class="row">
<div class="col-md-12 text-left">
<div id="payment-pending-block" class="payment-pending" style="display: none;">
<p class="payment-pending-title">
<span class="payment-spinner"></span>{:lang('sqpay.notice_title')}
</p>
<p class="payment-pending-text">{:lang('sqpay.notice_text')}</p>
<p class="payment-pending-text">
{:str_replace('%order_sn%', $order_sn ?? '', lang('pay.order_submitted'))}
</p>
<p class="payment-pending-status" id="payment-pending-status">{:lang('sqpay.notice_text')}</p>
<div class="register">
<a href="{$repay_url}">
<button class="dj-btn">{:lang('pay.repay_button')}</button>
</a>
</div>
</div>
<div id="payment-success-block">
<p style="font-size: 20px">{:lang('pay.thank_you_message')}</p>
<p style="font-size: 18px;color: #880000">
{:str_replace('%order_sn%', $order_sn ?? '', lang('pay.order_submitted'))}
</p>
<p style="font-size: 15px">
{:str_replace('%country%', $country ?? '', lang('pay.processing_time_info'))}
</p>
{if $arrive_date_info}
<p style="font-size: 15px">
{$arrive_date_info}
</p>
{/if}
<p style="font-size: 20px;font-weight: bolder;color: red">
{:lang('pay.contact')}
</p>
</div>
</div>
<div class="register" id="payment-success-action">
<a href="{:url('visa/index')}">
<button class="dj-btn">{:lang('pay.visa_application_button')}</button>
</a>
</div>
</div>
</div>
</div>
</div>
{include file="public/newfooter" }
<script>
(function () {
var pageEl = document.getElementById('payment-success-page');
if (!pageEl) {
return;
}
var isConfirmed = pageEl.getAttribute('data-payment-confirmed') === '1';
var pollUrl = pageEl.getAttribute('data-poll-status-url');
var pendingBlock = document.getElementById('payment-pending-block');
var successBlock = document.getElementById('payment-success-block');
var successAction = document.getElementById('payment-success-action');
var pendingStatus = document.getElementById('payment-pending-status');
var pendingText = "{:lang('sqpay.notice_text')}";
var successText = "{:lang('controller.payment_success')}";
var pollTimer = null;
var maxAttempts = 30;
var attempt = 0;
function showPending() {
if (pendingBlock) {
pendingBlock.style.display = 'block';
}
if (successBlock) {
successBlock.style.display = 'none';
}
if (successAction) {
successAction.style.display = 'none';
}
}
function showSuccess() {
if (pendingBlock) {
pendingBlock.style.display = 'none';
}
if (successBlock) {
successBlock.style.display = 'block';
}
if (successAction) {
successAction.style.display = 'block';
}
}
if (isConfirmed || !pollUrl) {
showSuccess();
return;
}
showPending();
async function pollStatus() {
attempt++;
try {
var response = await fetch(pollUrl, {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
var data = await response.json();
if (data.status === 1) {
if (pendingStatus) {
pendingStatus.textContent = successText;
}
window.clearInterval(pollTimer);
showSuccess();
return;
}
if (data.status === -1 && data.url) {
window.clearInterval(pollTimer);
window.location.href = data.url;
return;
}
if (pendingStatus) {
pendingStatus.textContent = data.msg || pendingText;
}
} catch (e) {
if (pendingStatus) {
pendingStatus.textContent = pendingText;
}
}
if (attempt >= maxAttempts) {
window.clearInterval(pollTimer);
if (pendingStatus) {
pendingStatus.textContent = pendingText;
}
}
}
pollStatus();
pollTimer = window.setInterval(pollStatus, 2000);
})();
</script>
</body>
</html>

View File

@@ -0,0 +1,34 @@
{include file="public/head" /}
<style>
body {
background-color: #f5f5f5;
}
.footer {
position: fixed;
bottom: 0;
}
</style>
<body>
<div class="wrap">
<div class="zh_content">
{include file="public/newnav" /}
</div>
<div class="layui-container">
<div class="container">
<section class="m-form-application-category-container active" style="margin-top: 20px;">
<div class="m-form-application-title">
<h2>{:lang('foot.cookie_policy')}</h2>
</div>
<div class="modular-row callout bg-grey">
<div class="row">
<div class="col-md-12 text-left" style="font-size: 18px">
{:lang('foot.cookie_policy_info')}
</div>
</div>
</div>
</section>
</div>
</div>
</div>
{include file="public/newfooter" /}
</body>

View File

@@ -0,0 +1,38 @@
{include file="public/head" /}
<style>
body {
background: #F8F8F8;
}
.footer {
position: fixed;
bottom: 0;
}
</style>
<body>
<div class="wrap">
<div class="zh_content">
{include file="public/newnav" /}
</div>
<div class="layui-container">
<div class="u-bg-greylight">
<div class="container">
<section class="m-form-application-category-container active" style="margin-top: 20px;">
<div class="m-form-application-title">
<h2>{:lang('foot.disclaimer')}</h2>
</div>
<div class="modular-row callout bg-grey">
<div class="row">
<div class="col-md-12 text-left" style="font-size: 18px">
{:lang('foot.disclaimer_info')}
</div>
</div>
</div>
</section>
</div>
</div>
</div>
</div>
{include file="public/newfooter" /}
</body>

View File

@@ -0,0 +1,37 @@
{include file="public/head" /}
<style>
.text-left h4 {
margin-top: 20px;
}
body {
background-color: #f5f5f5;
}
.footer {
position: fixed;
bottom: 0;
}
</style>
<body>
<div class="wrap">
<div class="zh_content">
{include file="public/newnav" /}
</div>
<div class="layui-container">
<div class="container">
<section class="m-form-application-category-container active" style="margin-top: 20px;">
<div class="m-form-application-title">
<h2>{:lang('foot.refund_policy')}</h2>
</div>
<div class="modular-row callout bg-grey">
<div class="row">
<div class="col-md-12 text-left" style="font-size: 18px">
{:lang('foot.refund_policy_info')}
</div>
</div>
</div>
</section>
</div>
</div>
</div>
{include file="public/newfooter" /}
</body>

View File

@@ -0,0 +1,35 @@
{include file="public/head" /}
<style>
body {
background: #F8F8F8;
}
.section {
padding: 0;
}
</style>
<body>
<div class="wrap">
<div class="zh_content">
{include file="public/newnav" /}
</div>
<div class="layui-container">
<div class="u-bg-greylight">
<div class="container">
<section class="m-form-application-category-container active" style="margin-top: 20px;">
<div class="m-form-application-title">
<h2>{:lang('foot.privacy_policy')}</h2>
</div>
<div class="modular-row callout bg-grey">
<div class="row">
<div class="col-md-12 text-left" style="font-size: 18px">
{:lang('foot.privacy_policy_info')}
</div>
</div>
</div>
</section>
</div>
</div>
</div>
</div>
{include file="public/newfooter" /}
</body>

View File

@@ -0,0 +1,71 @@
{include file="public/head" /}
<style>
.text-left h4 {
margin-top: 20px;
}
body {
background-color: #f5f5f5;
}
.card {
background: #ffffff;
padding: 40px 32px;
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.06);
}
h1 {
margin: 0 0 16px;
font-size: 32px;
line-height: 1.3;
color: #111827;
}
.intro {
margin: 0 0 28px;
color: #6b7280;
font-size: 16px;
}
section {
margin-top: 28px;
padding-top: 22px;
border-top: 1px solid #e5e7eb;
}
h2 {
margin: 0 0 12px;
font-size: 22px;
}
p {
margin: 0 0 12px;
}
.sub-list {
margin-top: 8px;
}
.contact a {
color: #0f766e;
text-decoration: none;
}
.contact a:hover {
text-decoration: underline;
}
</style>
<body>
<div class="wrap">
<div class="zh_content">
{include file="public/newnav" /}
</div>
<div class="layui-container">
<div class="container">
<section class="m-form-application-category-container active" style="margin-top: 20px;">
<div class="m-form-application-title">
<h2>{:lang('foot.refund_policy')}</h2>
</div>
<div class="modular-row callout bg-grey">
<div class="row">
<div class="col-md-12 text-left" style="font-size: 18px">
{:lang('foot.refund_policy_info')}
</div>
</div>
</div>
</section>
</div>
</div>
</div>
{include file="public/newfooter" /}
</body>

View File

@@ -0,0 +1,35 @@
{include file="public/head" /}
<style>
.text-left h4 {
margin-bottom: 20px;
}
body {
background: #F8F8F8;
}
</style>
<body>
<div class="wrap">
<div class="zh_content">
{include file="public/newnav" /}
</div>
<div class="layui-container">
<div class="u-bg-greylight">
<div class="container">
<section class="m-form-application-category-container active" style="margin-top: 20px;">
<div class="m-form-application-title">
<h2>{:lang('foot.user_agreement')}</h2>
</div>
<div class="modular-row callout bg-grey">
<div class="row">
<div class="col-md-12 text-left" style="font-size: 18px">
{:lang('foot.user_agreement_info')}
</div>
</div>
</div>
</section>
</div>
</div>
</div>
</div>
{include file="public/newfooter" /}
</body>

View File

@@ -0,0 +1,10 @@
<div class="footer">
<?= html_entity_decode($footer) ?>
<p><a href="#" class="termsCheckbox">{:lang('js.terms_conditions')}</a>&nbsp;|&nbsp;<a href="#" class="privacy_policy">{:lang('js.privacy_policy')}</a></p>
</div>
<style>
.footer a{
color: #ffffff;
}
</style>

View File

@@ -0,0 +1,98 @@
<!DOCTYPE html>
<html lang="" style="height:100%;">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{:lang('head.title')}</title>
<meta name="keywords" content="{:lang('head.keywords')}"/>
<meta name="description" content="{:lang('head.description')}"/>
<meta property="og:title" content="{:lang('head.og_title')}"/>
<meta property="og:description" content="{:lang('head.og_description')}"/>
<meta property="og:url" content="{$_SERVER['HTTP_HOST']}"/>
<meta property="og:type" content="website"/>
<link rel="icon" type="image/png" href="/static/image/icon.png">
<link rel="stylesheet" href="/static/layui/css/layui.css">
<script src="/static/layui/layui.js"></script>
<script src="/static/js/jquery.js"></script>
<script>
(function () {
function detectBasePath() {
var pathname = window.location.pathname || '';
var match = pathname.match(/^\/[^\/]+(?=\/home\/|\/static\/|\/json\/|\/$)/);
return match ? match[0] : '';
}
function normalizeBasePath(base) {
base = (base || '').trim();
// 模板变量未渲染时,退回到 URL 推断
if (!base || base === '/' || base.charAt(0) === '{') {
base = detectBasePath();
}
return base.replace(/\/+$/, '');
}
window.__BASE_PATH__ = normalizeBasePath('{$base_path}');
window.withBase = function (url) {
if (!url) {
return url;
}
if (/^(?:[a-z][a-z0-9+\-.]*:)?\/\//i.test(url) || /^[a-z][a-z0-9+\-.]*:/i.test(url)) {
return url;
}
if (url.charAt(0) !== '/') {
return url;
}
var base = window.__BASE_PATH__ || '';
if (!base) {
return url;
}
if (url === base || url.indexOf(base + '/') === 0) {
return url;
}
return base + url;
};
function attachAjaxPrefilter(jq) {
if (!jq || !jq.ajaxPrefilter || jq.__with_base_prefilter_attached__) {
return;
}
jq.__with_base_prefilter_attached__ = true;
jq.ajaxPrefilter(function (options) {
if (options && options.url) {
options.url = window.withBase(options.url);
}
});
}
attachAjaxPrefilter(window.jQuery);
attachAjaxPrefilter(window.$);
// 兼容 layui 可能持有独立 jQuery 实例
if (window.layui && window.layui.$) {
attachAjaxPrefilter(window.layui.$);
}
})();
</script>
<script src="/static/js/pub.js"></script>
<link rel="stylesheet" href="/static/css/tur_main.css?v={:time()}">
<link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link href="/static/bootstrap/css/bootstrap-icons.min.css" rel="stylesheet">
<link href="/static/css/newin.css?v={:time()}" rel="stylesheet">
<script type="text/javascript" async="" src="/static/js/footer.js"></script>
<script type="text/javascript" async="" src="/static/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="/static/js/passport-ocr-sdk.min.js?v=1"></script>
<script>
$(function(){
var footHight=$(".footer").height();
var docHeight=document.documentElement.clientHeight;
var bodyHeight=$("body").height();
var subHeight=docHeight-bodyHeight-footHight;
if(subHeight>30){
$(".footer").css({"position":"fixed","bottom":"0","z-index":"1000"});
$("body").css({"padding-bottom":"142px"});
} else {
$(".footer").css({"position": "static"});
$("body").css({"padding-bottom": "0"});
}
$(".footer").show();
})
</script>
</head>

View File

@@ -0,0 +1,46 @@
{include file="public/head"}
<link rel="stylesheet" href="/static/css/jump.css?v=1">
<body class="jump-page">
<div class="zh_content">
{include file="public/newnav" }
</div>
<main class="jump-shell">
<section class="jump-panel jump-panel--{$type|default='success'}">
<div class="jump-icon" aria-hidden="true"></div>
<div class="jump-content">
<h1 class="jump-message">{$msg}</h1>
<p class="jump-countdown">
<span id="countdown">{$wait|default=3}</span> {:lang('jump.auto_redirect')}...
</p>
</div>
<div class="jump-actions">
<a href="{$url ?: 'javascript:history.back();'}" class="jump-button">{:lang('jump.redirect_now')}</a>
</div>
</section>
</main>
{include file="public/newfooter" }
<script>
// 倒计时显示
let time = {$wait|default=3};
const countdownEl = document.getElementById('countdown');
const targetUrl = "{$url|default=''}";
const timer = setInterval(() => {
time--;
countdownEl.textContent = time;
if (time <= 0) {
clearInterval(timer);
if (targetUrl) {
location.href = targetUrl;
} else {
history.back();
}
}
}, 1000);
</script>
</body>
</html>

View File

@@ -0,0 +1,136 @@
<div class="process" style="background: none;margin-top: 0">
<div class="ps-tit" style="color: #151423; font-weight: 600; font-size: 32px; letter-spacing: .2px;">
{:lang('index.why_choose_us_title')}
<p style="font-size: 15px; padding: 10px;">
{:lang('index.why_choose_us_subtitle')}
</p>
</div>
</div>
<section class="middle-box2">
<!-- <div class="">-->
<!-- <div class="image-box">-->
<!-- <img class="" src="__TMPL__/public/static/image/why-choose-us.webp">-->
<!-- </div>-->
<!-- <h2 class="">为什么选择我们?</h2>-->
<!-- <p class="">了解为什么我们在旅行证件行业处于领先地位,使旅客能够轻松飞行。</p>-->
<!-- <div class="mt30">-->
<!-- <a href="{:url('visa/index')}" tabindex="-1">-->
<!-- <button-->
<!-- class="">-->
<!-- 立即申请-->
<!-- <svg class="wh16" fill="currentColor"-->
<!-- xmlns="http://www.w3.org/2000/svg"-->
<!-- viewBox="0 0 448 512">-->
<!-- <path-->
<!-- d="M190.5 66.9l22.2-22.2c9.4-9.4 24.6-9.4 33.9 0L441 239c9.4 9.4 9.4 24.6 0 33.9L246.6 467.3c-9.4 9.4-24.6 9.4-33.9 0l-22.2-22.2c-9.5-9.5-9.3-25 .4-34.3L311.4 296H24c-13.3 0-24-10.7-24-24v-32c0-13.3 10.7-24 24-24h287.4L190.9 101.2c-9.8-9.3-10-24.8-.4-34.3z">-->
<!-- </path>-->
<!-- </svg>-->
<!-- </button>-->
<!-- </a>-->
<!-- </div>-->
<!-- </div>-->
<div class="right-box">
<div
class="item">
<svg class=""
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<mask id="a" width="20" height="20" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha">
<path fill="#D9D9D9" d="M0 0h20v20H0z"></path>
</mask>
<g>
<path fill="currentColor"
d="M10 2a8 8 0 1 1 0 16 8 8 0 0 1 0-16Zm-.75 3.75V10c0 .25.125.484.334.625l3 2a.748.748 0 0 0 1.041-.21.748.748 0 0 0-.21-1.04L10.75 9.6V5.75A.748.748 0 0 0 10 5a.748.748 0 0 0-.75.75Z">
</path>
</g>
</svg>
<div class="">
<h6 class="">{:lang('index.convenient')}</h6>
<p class="">{:lang('index.convenient_desc')}</p>
</div>
</div>
<div
class="item">
<svg class=""
xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
<path
d="M7 10a4 4 0 1 0 0-8 4 4 0 0 0 0 8Zm-1.428 1.5A5.57 5.57 0 0 0 0 17.072c0 .512.416.928.928.928h12.144c.056 0 .11-.006.166-.016-2.385-1.722-3.12-4.406-3.222-6.256A5.533 5.533 0 0 0 8.43 11.5h-2.86Zm9.65-2.447-3.75 1.5a.754.754 0 0 0-.472.697c0 1.978.81 5.275 4.213 6.694a.742.742 0 0 0 .578 0C19.19 16.525 20 13.228 20 11.25a.754.754 0 0 0-.472-.697l-3.75-1.5a.744.744 0 0 0-.556 0Zm3.26 2.697c-.123 1.584-.85 3.647-2.982 4.678v-5.872l2.981 1.194Z">
</path>
</svg>
<div class="">
<h6 class="">{:lang('index.secure')}</h6>
<p class="">{:lang('index.secure_desc')}</p>
</div>
</div>
<div
class="item">
<svg class=""
xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
<path
d="M1.2 4.8c0-1.324 1.076-2.4 2.4-2.4h6v4.8c0 .664.536 1.2 1.2 1.2h4.8v1.448a6.602 6.602 0 0 0-4.8 6.352 6.6 6.6 0 0 0 2.764 5.374c-.12.018-.24.026-.364.026H3.6a2.402 2.402 0 0 1-2.4-2.4V4.8Zm14.4 2.4h-4.8V2.4l4.8 4.8Zm-3.6 9a5.4 5.4 0 1 1 10.8 0 5.4 5.4 0 0 1-10.8 0Zm7.924-1.624a.602.602 0 0 0-.848 0L16.8 16.852l-1.076-1.076a.602.602 0 0 0-.848 0 .602.602 0 0 0 0 .848l1.5 1.5a.602.602 0 0 0 .848 0l2.7-2.7a.602.602 0 0 0 0-.848Z">
</path>
</svg>
<div class="">
<h6 class="">{:lang('index.approval')}</h6>
<p class="">{:lang('index.approval_desc')}</p>
</div>
</div>
<div
class="item">
<svg class="" width="20"
height="20" viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path id="Vector"
d="M10 18C12.1217 18 14.1566 17.1571 15.6569 15.6569C17.1571 14.1566 18 12.1217 18 10C18 7.87827 17.1571 5.84344 15.6569 4.34315C14.1566 2.84285 12.1217 2 10 2C7.87827 2 5.84344 2.84285 4.34315 4.34315C2.84285 5.84344 2 7.87827 2 10C2 12.1217 2.84285 14.1566 4.34315 15.6569C5.84344 17.1571 7.87827 18 10 18ZM7.30625 7.16563C7.55312 6.46875 8.21563 6 8.95625 6H10.7781C11.8687 6 12.75 6.88438 12.75 7.97188C12.75 8.67813 12.3719 9.33125 11.7594 9.68437L10.75 10.2625C10.7437 10.6687 10.4094 11 10 11C9.58437 11 9.25 10.6656 9.25 10.25V9.82812C9.25 9.55937 9.39375 9.3125 9.62813 9.17812L11.0125 8.38438C11.1594 8.3 11.25 8.14375 11.25 7.975C11.25 7.7125 11.0375 7.50313 10.7781 7.50313H8.95625C8.85 7.50313 8.75625 7.56875 8.72188 7.66875L8.70937 7.70625C8.57188 8.09688 8.14062 8.3 7.75313 8.1625C7.36563 8.025 7.15938 7.59375 7.29688 7.20625L7.30937 7.16875L7.30625 7.16563ZM9 13C9 12.7348 9.10536 12.4804 9.29289 12.2929C9.48043 12.1054 9.73478 12 10 12C10.2652 12 10.5196 12.1054 10.7071 12.2929C10.8946 12.4804 11 12.7348 11 13C11 13.2652 10.8946 13.5196 10.7071 13.7071C10.5196 13.8946 10.2652 14 10 14C9.73478 14 9.48043 13.8946 9.29289 13.7071C9.10536 13.5196 9 13.2652 9 13Z">
</path>
</svg>
<div class="">
<h6 class="">{:lang('index.support')}</h6>
<p class="">{:lang('index.support_desc')}</p>
</div>
</div>
<div
class="item">
<svg xmlns="http://www.w3.org/2000/svg" class="" viewBox="0 0 512 512" fill="currentColor">
<path d="M258.9 48C141.92 46.42 46.42 141.92 48 258.9c1.56 112.19 92.91 203.54 205.1 205.1 117 1.6 212.48-93.9 210.88-210.88C462.44 140.91 371.09 49.56 258.9 48zm-3.68 152.11c.21-1.2.44-2.4.71-3.59a66.46 66.46 0 0116.29-31.21c12.89-13.73 31.16-21.31 51.45-21.31a74.05 74.05 0 0125.06 4.26 66.69 66.69 0 0126.27 17.2 68.15 68.15 0 0118 42.14 78.46 78.46 0 010 11.4 86.19 86.19 0 01-8.2 31q-.76 1.59-1.59 3.15c-1.11 2.07-2.3 4.1-3.58 6.06a79.47 79.47 0 01-8.63 11c-13.12 14-29.92 21.73-47.31 21.73a59.61 59.61 0 01-19.17-3.18 63.47 63.47 0 01-6.1-2.43 70.76 70.76 0 01-22.07-16.12 83.76 83.76 0 01-22-51.32q-.27-3.88-.18-7.68a75.62 75.62 0 011.05-11.08zm-149.73 24.34a59.87 59.87 0 015.2-20.64 56.76 56.76 0 012.78-5.3 54.49 54.49 0 017.19-9.56 55.62 55.62 0 0114-10.82 56.84 56.84 0 018.11-3.64 63.85 63.85 0 0133.35-2.39 57 57 0 0130.78 17 57.86 57.86 0 0115.41 38.62c.05 2.11 0 4.23-.15 6.38a71.58 71.58 0 01-6 23.84 69.49 69.49 0 01-5.73 10.42 65.39 65.39 0 01-15.76 16.57c-1.5 1.07-3.06 2.07-4.67 3.07a54.21 54.21 0 01-10 4.65 49.31 49.31 0 01-16.2 2.76c-.93 0-1.86 0-2.78-.08a47.6 47.6 0 01-5.48-.62 51.19 51.19 0 01-5.35-1.23 53.54 53.54 0 01-7.72-2.89c-.84-.39-1.66-.8-2.48-1.23-18-9.49-31.57-29.16-34.23-52.12-.12-1.05-.22-2.1-.29-3.16a66.59 66.59 0 01.02-9.63zm53.92 178.6a177.27 177.27 0 01-61.94-70.65 4 4 0 011.62-5.26C117.67 316.69 141.4 311 163.82 311c17 0 30.7 2 42.69 5.88a8 8 0 012.59 13.77c-23.35 19-38.4 42.54-45.47 70.75a2.77 2.77 0 01-4.22 1.65zM256 432a175.12 175.12 0 01-65.7-12.72 4 4 0 01-2.4-4.46c.4-2.05.84-3.92 1.23-5.48 7.12-28.43 24.76-52 51-68.18 23.29-14.35 53-22.25 83.52-22.25 31.16 0 60 7.58 83.48 21.91a2.72 2.72 0 01.91 3.67A176.1 176.1 0 01256 432z"/>
<path d="M161 295.28a47.6 47.6 0 01-5.48-.62 47.6 47.6 0 005.48.62zM134.64 178.13a55.62 55.62 0 00-14 10.82 54.49 54.49 0 00-7.19 9.56 54.49 54.49 0 017.19-9.56 55.62 55.62 0 0114-10.82zM216.17 257.89a71.58 71.58 0 006-23.84c.15-2.15.2-4.27.15-6.38q.08 3.15-.15 6.38a71.58 71.58 0 01-6 23.84zM134.64 178.13a56.84 56.84 0 018.11-3.64 56.84 56.84 0 00-8.11 3.64zM150.21 293.43a53.54 53.54 0 01-7.72-2.89 53.54 53.54 0 007.72 2.89zM105.78 237.19c2.66 23 16.26 42.63 34.23 52.12-18.01-9.49-31.57-29.16-34.23-52.12zM254.34 219a83.76 83.76 0 0022 51.32 70.76 70.76 0 0022.07 16.12 70.76 70.76 0 01-22.07-16.12 83.76 83.76 0 01-22-51.32q-.27-3.88-.18-7.68-.09 3.75.18 7.68zM304.5 288.82a63.47 63.47 0 01-6.1-2.43 63.47 63.47 0 006.1 2.43zM255.93 196.54a66.46 66.46 0 0116.29-31.21 66.46 66.46 0 00-16.29 31.21zM375 165.46a68.15 68.15 0 0118 42.14 68.15 68.15 0 00-18-42.14 66.69 66.69 0 00-26.27-17.2 66.69 66.69 0 0126.27 17.2zM393 219a86.19 86.19 0 01-8.2 31 86.19 86.19 0 008.2-31zM254.16 211.27a75.62 75.62 0 011.06-11.14 75.62 75.62 0 00-1.06 11.14zM383.19 253.16zM206.88 189.05a57.86 57.86 0 0115.41 38.62 57.86 57.86 0 00-15.41-38.62 57 57 0 00-30.78-17 57 57 0 0130.78 17zM190 288a54.21 54.21 0 01-10 4.65 54.21 54.21 0 0010-4.65zM105.49 224.45a59.87 59.87 0 015.2-20.64 59.87 59.87 0 00-5.2 20.64zM194.68 284.88C193.17 286 191.61 287 190 288c1.61-1 3.17-2 4.68-3.12zM216.17 257.89a69.49 69.49 0 01-5.73 10.42 69.49 69.49 0 005.73-10.42zM110.69 203.81a56.76 56.76 0 012.78-5.3 56.76 56.76 0 00-2.78 5.3zM194.68 284.88a65.39 65.39 0 0015.76-16.57 65.39 65.39 0 01-15.76 16.57z"/>
</svg>
<div class="">
<h6 class="">{:lang('index.clients')}</h6>
<p class="">{:lang('index.clients_desc')}</p>
</div>
</div>
<div
class="item">
<svg xmlns="http://www.w3.org/2000/svg" class="" viewBox="0 0 512 512" fill="currentColor">
<path d="M479.07 111.36a16 16 0 00-13.15-14.74c-86.5-15.52-122.61-26.74-203.33-63.2a16 16 0 00-13.18 0C168.69 69.88 132.58 81.1 46.08 96.62a16 16 0 00-13.15 14.74c-3.85 61.11 4.36 118.05 24.43 169.24A349.47 349.47 0 00129 393.11c53.47 56.73 110.24 81.37 121.07 85.73a16 16 0 0012 0c10.83-4.36 67.6-29 121.07-85.73a349.47 349.47 0 0071.5-112.51c20.07-51.19 28.28-108.13 24.43-169.24zm-131 75.11l-110.8 128a16 16 0 01-11.41 5.53h-.66a16 16 0 01-11.2-4.57l-49.2-48.2a16 16 0 1122.4-22.86l37 36.29 99.7-115.13a16 16 0 0124.2 20.94z"/>
</svg>
<div class="">
<h6 class="">{:lang('index.service')}</h6>
<p class="">{:lang('index.service_desc')}</p>
</div>
</div>
<div
class="item">
<svg xmlns="http://www.w3.org/2000/svg" class="" viewBox="0 0 512 512" fill="currentColor">
<path d="M432.8 211.44c-15.52-8.82-34.91-2.28-43.31 13.68l-41.38 84.41a7 7 0 01-8.93 3.43 7 7 0 01-4.41-6.52V72c0-13.91-12.85-24-26.77-24s-26 10.09-26 24v156.64A11.24 11.24 0 01271.21 240 11 11 0 01260 229V24c0-13.91-10.94-24-24.86-24S210 10.09 210 24v204.64A11.24 11.24 0 01199.21 240 11 11 0 01188 229V56c0-13.91-12.08-24-26-24s-26 11.09-26 25v187.64A11.24 11.24 0 01125.21 256 11 11 0 01114 245V120c0-13.91-11.08-24-25-24s-25.12 10.22-25 24v216c0 117.41 72 176 160 176h16c88 0 115.71-39.6 136-88l68.71-169c6.62-18 3.6-34.75-11.91-43.56z"/>
</svg>
<div class="">
<h6 class="">{:lang('index.experience')}</h6>
<p class="">{:lang('index.experience_desc')}</p>
</div>
</div>
<div
class="item">
<svg xmlns="http://www.w3.org/2000/svg" class="" viewBox="0 0 512 512" fill="currentColor">
<path d="M256 448a32 32 0 01-18-5.57c-78.59-53.35-112.62-89.93-131.39-112.8-40-48.75-59.15-98.8-58.61-153C48.63 114.52 98.46 64 159.08 64c44.08 0 74.61 24.83 92.39 45.51a6 6 0 009.06 0C278.31 88.81 308.84 64 352.92 64c60.62 0 110.45 50.52 111.08 112.64.54 54.21-18.63 104.26-58.61 153-18.77 22.87-52.8 59.45-131.39 112.8a32 32 0 01-18 5.56z"/>
</svg>
<div class="">
<h6 class="">{:lang('index.approval_rate')}</h6>
<p class="">{:lang('index.approval_rate_desc')}</p>
</div>
</div>
</div>
</section>

View File

@@ -0,0 +1,284 @@
<style>
.zh_top {
display: flex;
padding-top: 5px;
padding-bottom: 10px;
}
.zh_head {
width: 40%;
justify-content: right;
}
.zh_hcon {
margin: unset;
width: unset;
}
.zh_nav {
width: 50%;
height: 70px;
position: relative;
}
@media (max-width: 1620px) and (min-width: 1201px){
.zh_nav {
width: 68%;
height: 70px;
position: relative;
}
}
@media (max-width: 1200px) {
.language-switcher {
top: 20%;
}
.zh_nav_btn {
display: block;
width: 30px;
height: 30px;
position: absolute;
right: 10px;
top: 20px;
cursor: pointer;
z-index: 1001;
}
.zh_nav {
width: 100%;
position: absolute;
left: 0;
background-color: var(--main-color);
display: none;
z-index: 1001;
}
/* 点击展开时显示 */
.zh_nav.active {
display: block;
}
.nav_con {
display: flex;
flex-direction: column;
width: 100%;
padding: 0;
margin: 0;
}
.nav_con li {
width: 100%;
border-bottom: 1px solid #eee;
list-style: none;
}
.nav_con li a {
display: block;
width: 100%;
text-align: center;
color: #fff;
text-decoration: none;
font-size: 15px;
background-color: var(--main-color);
}
.nav_con li a:hover {
background-color: #f8f8f8;
}
/* 语言切换按钮放在导航底部 */
.nav-language-switcher {
position: unset !important;
width: 100%;
padding: 10px 15px;
background-color: var(--main-color);
display: flex;
justify-content: flex-start;
}
.language-btn {
font-size: 13px;
padding: 5px 10px;
height: auto;
}
.language-dropdown {
position: relative;
width: auto;
border: none;
box-shadow: none;
}
.language-dropdown a {
padding: 8px 10px;
font-size: 13px;
}
}
/* 语言切换按钮容器 */
.language-switcher {
position: absolute;
display: inline-block;
right: 20%;
}
/* 语言切换按钮样式 */
.language-btn {
background-color: #f8f8f8;
border: 1px solid #ddd;
border-radius: 4px;
padding: 5px 10px;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
height: 40px;
}
.language-btn:hover {
background-color: #e8e8e8;
}
/* 下拉菜单样式 */
.language-dropdown {
display: none;
position: absolute;
/*right: 0;*/
top: 100%;
background-color: white;
border: 1px solid #ddd;
border-radius: 4px;
/*width: 100%;*/
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
z-index: 1000;
}
.language-dropdown a {
padding: 5px;
width: 100%;
display: block;
text-decoration: none;
color: #333;
font-size: 14px;
float: unset;
line-height: 40px;
}
.language-dropdown a:hover {
background-color: #f5f5f5;
display: block;
text-decoration: none;
color: #333;
font-size: 14px;
width: 100%;
float: unset;
line-height: 40px;
}
/* 显示下拉菜单 */
.show-dropdown {
display: block;
}
ol, ul, dl {
margin-bottom: 0;
}
.nav_con {
padding: 0;
}
/* ===== 手机端语言切换显示控制 ===== */
@media (max-width: 1200px) {
/* 手机端显示 languageBtn1顶部 */
#language-switcher1 {
display: inline-flex !important;
}
/* 隐藏导航栏中的 languageBtn */
#languageBtn {
display: none !important;
}
}
/* ===== PC端反向控制 ===== */
@media (min-width: 1201px) {
/* PC端隐藏顶部语言切换显示导航栏内的 */
#language-switcher1 {
display: none !important;
}
#languageBtn {
display: inline-flex !important;
}
}
</style>
<div class="zh_top">
<div class="zh_head">
<div class="zh_hcon">
<div class="zh_logo"><a href="/"><img src="/static/image/logo.png" alt=""></a></div>
</div>
</div>
<div class="language-switcher clearfix-language-switcher" id="language-switcher1" style="display:none;">
<button class="language-btn" id="languageBtn1">
<span>{if $think_lang == 'en-us'}English{else/}中文 {/if}</span>
<span style="margin-left: 5px;"></span>
</button>
<div class="language-dropdown" id="languageDropdown1">
<a class="language" href="#" data-id="zh-cn">中文</a>
<a class="language" href="#" data-id="en-us">English</a>
</div>
</div>
<img class="zh_nav_btn" src="/static/image/menu.png" alt="">
<div class="zh_nav">
<ul class="nav_con">
<li class=""><a href="/">{:lang('index.home')}</a></li>
<li class=""><a href="{:url('visa/index')}">{:lang('index.online_application')}</a></li>
<li class=""><a href="{:url('lookup/index')}">{:lang('index.entry_card_query')}</a></li>
<li class=""><a href="{:url('contact/index')}">{:lang('contact.menu_contact')}</a></li>
<li class=""><a href="{:url('news/index')}">{:lang('index.usage_help')}</a></li>
<div class="language-switcher nav-language-switcher">
<button class="language-btn" id="languageBtn">
<span>{if $think_lang == 'en-us'}English{else/}中文 {/if}</span>
<span style="margin-left: 5px;"></span>
</button>
<div class="language-dropdown" id="languageDropdown">
<a class="language" href="#" data-id="zh-cn">中文</a>
<a class="language" href="#" data-id="en-us">English</a>
</div>
</div>
</ul>
</div>
</div>
<script>
$('.zh_nav_btn').click(function () {
$('.zh_nav').toggleClass('active');
});
</script>
<div class="swiper" style="height: 100px;background: var(--main-color)">
</div>
<script>
const languageBtn = document.getElementById('languageBtn');
const languageDropdown = document.getElementById('languageDropdown');
// 切换下拉菜单显示/隐藏
languageBtn.addEventListener('click', function() {
languageDropdown.classList.toggle('show-dropdown');
});
// 点击页面其他地方关闭下拉菜单
document.addEventListener('click', function(e) {
if (!languageBtn.contains(e.target) && !languageDropdown.contains(e.target)) {
languageDropdown.classList.remove('show-dropdown');
}
});
const languageBtn1 = document.getElementById('languageBtn1');
const languageDropdown1 = document.getElementById('languageDropdown1');
// 切换下拉菜单显示/隐藏
languageBtn1.addEventListener('click', function() {
languageDropdown1.classList.toggle('show-dropdown');
});
// 点击页面其他地方关闭下拉菜单
document.addEventListener('click', function(e) {
if (!languageBtn1.contains(e.target) && !languageDropdown1.contains(e.target)) {
languageDropdown1.classList.remove('show-dropdown');
}
});
$('.language').click(function () {
const lang = $(this).attr('data-id');
$.ajax({
url: '/home/index/langset',
type: 'post',
data: {
lang: lang
},
success: function (res) {
if (res.code === 1) {
window.location.reload();
}
}
})
})
</script>

View File

@@ -0,0 +1,103 @@
<footer>
<div class="container">
<div class="text-center">
<?= html_entity_decode($footer) ?>
<div>{if $company_address}{:lang('foot.company_address')}{$company_address}{/if}</div>
</div>
<div class="footer-divider"></div>
<div class="d-flex flex-column flex-md-row justify-content-between align-items-center gap-2">
<div></div>
<!-- <div class="d-flex gap-3">-->
<!-- <a href="/home/policy/user"><span>{:lang('foot.user_agreement')}</span></a>-->
<!-- <a href="/home/policy/privacy"><span>{:lang('foot.privacy_policy')}</span></a>-->
<!-- <a href="/home/policy/disclaimer"><span>{:lang('foot.disclaimer')}</span></a>-->
<!-- <a href="/home/policy/refund"><span>{:lang('foot.refund_policy')}</span></a>-->
<!-- <a href="/home/policy/cookie"><span>{:lang('foot.cookie_policy')}</span></a>-->
<!-- </div>-->
</div>
</div>
</footer>
<!-- ===== 新增:悬浮客服/返回顶部(小模块很实用) ===== -->
<div class="float-help">
<a class="float-btn" href="/home/contact" title="{:lang('contact.menu_contact')}"><i class="bi bi-chat-dots"></i></a>
<a class="float-btn" href="#" title="{:lang('contact.back_to_top')}" onclick="window.scrollTo({top:0,behavior:'smooth'});return false;">
<i class="bi bi-arrow-up"></i>
</a>
</div>
<script>
// 监听复选框点击
$('.termsCheckbox').click(function (e) {
e.preventDefault();
if (window.innerWidth <= 768) {
area = ['90%', '90%']
} else {
area = ['50%', '60%']
}
// 弹出条款内容的弹窗
layer.open({
type: 1, // 弹窗类型:页面层
title: '{:lang(\'js.terms_conditions\')}', content: `
<div style="padding: 20px;">
<h3>{:lang(\'js.terms_conditions\')}</h3>
<p></p><p><span style="font-size: 12px;">{:str_replace(\'%country%\', $country ?? \'\', lang(\'js.terms_intro\'))}</span></p>
<p><span style="font-size: 12px;">{:str_replace(\'%country%\', $country ?? \'\', lang(\'js.our_services\'))}</span></p>
<p><span style="font-size: 12px;">{:str_replace(\'%country%\', $country ?? \'\', lang(\'js.application_process\'))}</span></p>
<p><span style="font-size: 12px;">{:lang(\'js.read_confirmation\')}</span></p>
<p><br></p><p></p>
</div>
`, area: area, // 弹窗大小
btn: ['关闭'], // 按钮
yes: function (index) {
layer.close(index); // 关闭弹窗
}
});
});
$('.privacy_policy').click(function (e) {
e.preventDefault();
if (window.innerWidth <= 768) {
area = ['90%', '90%']
} else {
area = ['50%', '60%']
}
// 弹出条款内容的弹窗
layer.open({
type: 1, // 弹窗类型:页面层
title: '{:lang(\'js.privacy_policy\')}', content: `
<div style="padding: 20px;">
<h3>{:lang(\'js.privacy_policy\')}</h3>
<p></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.privacy_intro\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">1. {:lang(\'js.scope\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.scope_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">2. {:lang(\'js.information_use\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_use_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">3. {:lang(\'js.information_disclosure\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_disclosure_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">4. {:lang(\'js.information_storage\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_storage_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">5. {:lang(\'js.cookie_use\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.cookie_use_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">6. {:lang(\'js.information_security\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.information_security_content\')}</p>
<h3 style="box-sizing: border-box; margin: 2px 0px; padding: 10px 0px 0px; border: 0px; font-weight: 400; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.1; color: rgb(0, 48, 73); white-space: normal; background-color: rgb(255, 255, 255);">7. {:lang(\'js.policy_changes\')}</h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; padding: 0px; border: 0px; font-family: "Microsoft YaHei", Arial, Helvetica, sans-serif; font-size: 12px; color: rgb(51, 51, 51); white-space: normal; background-color: rgb(255, 255, 255);">{:lang(\'js.policy_changes_content\')}</p>
<p><br></p><p></p>
</div>
`, area: area, // 弹窗大小
btn: ['关闭'], // 按钮
yes: function (index) {
layer.close(index); // 关闭弹窗
}
});
});
</script>

View File

@@ -0,0 +1,73 @@
<!-- ===== Topbar / Navbar ===== -->
<header class="topbar">
<nav class="navbar navbar-expand-xl">
<div class="container py-2 topbar-inner">
<!-- 顶栏:永远一行 -->
<div class="topbar-head">
<a class="brand-wrap" href="#">
<span class="brand-badge brand-badge-logo"></span>
<span>
<div class="brand-title">{$country}TDAC</div>
<div class="brand-sub">Online Application Service</div>
</span>
</a>
<div class="topbar-right">
<!-- 手机端语言:始终显示,不进折叠菜单 -->
<div class="dropdown lang-mobile d-lg-none">
<button class="btn btn-sm lang-btn dropdown-toggle" data-bs-toggle="dropdown">{if $think_lang == 'en-us'}English{else/}中文 {/if}</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item language" data-id="zh-cn" href="#">中文</a></li>
<li><a class="dropdown-item language" data-id="en-us" href="#">English</a></li>
</ul>
</div>
<button class="navbar-toggler d-xl-none text-white border-0" type="button"
data-bs-toggle="collapse" data-bs-target="#nav">
<span class="navbar-toggler-icon" style="filter: invert(1)"></span>
</button>
</div>
</div>
<!-- 折叠菜单:永远在下一行展开 -->
<div class="collapse navbar-collapse" id="nav">
<ul class="navbar-nav ms-auto align-items-lg-center gap-lg-2 mt-2 mt-lg-0">
<li class="nav-item"><a class="nav-link" href="{$base_path}/">{:lang('index.home')}</a></li>
<li class="nav-item"><a class="nav-link" href="{:url('visa/index')}">{:lang('index.online_application')}</a></li>
<li class="nav-item"><a class="nav-link" href="{:url('lookup/index')}">{:lang('index.entry_card_query')}</a></li>
<li class="nav-item"><a class="nav-link" href="{:url('contact/index')}">{:lang('contact.menu_contact')}</a></li>
<li class="nav-item"><a class="nav-link" href="{:url('news/index')}">{:lang('index.usage_help')}</a></li>
<!-- PC 端语言:仍放在菜单里(手机端隐藏避免重复) -->
<li class="nav-item dropdown ms-lg-2 d-none d-lg-block">
<button class="btn btn-sm lang-btn dropdown-toggle" data-bs-toggle="dropdown">{if $think_lang == 'en-us'}English{else/}中文 {/if}</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item language" data-id="zh-cn" href="#">中文</a></li>
<li><a class="dropdown-item language" data-id="en-us" href="#">English</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
</header>
<script>
$('.language').click(function () {
const lang = $(this).attr('data-id');
$.ajax({
url: '/home/index/langset',
type: 'post',
data: {
lang: lang
},
success: function (res) {
if (res.code === 1) {
window.location.reload();
}
}
})
})
</script>

View File

@@ -0,0 +1,134 @@
{include file="public/head"}
<link rel="stylesheet" href="/static/css/page-sqpay.css?v=1">
<script type="text/javascript" src="https://web.squarecdn.com/v1/square.js"></script>
<script type="text/javascript">
window.applicationId = "{$applicationId}";
window.locationId = "{$locationId}";
window.currency = "{$currency}";
window.country = "{$pay_country}";
window.idempotencyKey = "{$uid}";
</script>
</head>
<body class="sqpay-page">
<div class="sqpay-layout">
{include file="public/newnav"}
<main class="sqpay-main">
<div class="sqpay-shell">
<section class="sqpay-card">
<div class="sqpay-heading">
<h1 class="sqpay-title">{:lang('sqpay.payment_title')}</h1>
<p class="sqpay-order">
{:str_replace('%order_sn%', $order_sn ?? '', lang('pay.order_submitted'))}
</p>
</div>
<div class="sqpay-amount">
<p class="sqpay-amount-label">{:lang('sqpay.total_amount_label')}</p>
<p class="sqpay-amount-value">{$total_fee} {$currency}</p>
</div>
<div class="sqpay-grid">
<section class="sqpay-method">
<header class="sqpay-method-head">
<h2 class="sqpay-method-title">{:lang('sqpay.card_pay_title')}</h2>
<div class="sqpay-brand-list">
<img alt="Visa" src="/static/image/pay/visa.png">
<img alt="Mastercard" src="/static/image/pay/logo_mastercard.png">
<img alt="JCB" src="/static/image/pay/logo_jcb.png">
<img alt="Discover" src="/static/image/pay/logo_discover.png">
<img alt="Diners Club" src="/static/image/pay/logo_diners.png">
<img alt="CB" src="/static/image/pay/logo_cb.png">
<img alt="Amex" src="/static/image/pay/logo_amex.png">
</div>
</header>
<div class="sqpay-method-body">
<form class="payment-form" id="fast-checkout" autocomplete="off">
<div id="card-container"></div>
<button id="card-button" class="sqpay-pay-button" type="button">{:lang('sqpay.pay_now')}</button>
</form>
</div>
</section>
<aside class="sqpay-notice">
<h3 class="sqpay-notice-head">{:lang('sqpay.notice_title')}</h3>
<p class="sqpay-notice-body">{:lang('sqpay.notice_text')}</p>
</aside>
</div>
</section>
</div>
</main>
{include file="public/newfooter"}
</div>
<script>
const order_sn = "{$order_sn}";
const total_price = "{$total_price}";
const paySuccessText = "{:lang('sqpay.pay_success')}";
const payFailedText = "{:lang('sqpay.pay_failed')}";
async function CardPay(fieldEl, buttonEl) {
// Create a card payment object and attach to page
const card = await window.payments.card();
await card.attach(fieldEl);
async function eventHandler(event) {
try {
const result = await card.tokenize();
if (result.status === 'OK') {
window.createPayment(result.token);
}
} catch (e) {
console.log( e.message)
layer.msg(payFailedText, {icon: 2, time: 2000})
layer.closeAll();
}
}
buttonEl.addEventListener('click', eventHandler);
layer.closeAll();
}
async function SquarePaymentFlow() {
var load = layer.load(2, {shade: 0.3})
CardPay(document.getElementById('card-container'), document.getElementById('card-button'));
}
window.payments = Square.payments(window.applicationId, window.locationId);
window.createPayment = async function (token) {
var load = layer.load(2, {shade: 0.3})
const dataJsonString = JSON.stringify({
token,
idempotencyKey: window.idempotencyKey,
order_sn: order_sn,
amount: total_price,
});
try {
const response = await fetch(`{:url('sqpay/paySquare')}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: dataJsonString
});
const data = await response.json();
if (data['code'] == 1) {
// 跳转成功页面
layer.msg(paySuccessText, {icon: 1, time: 2000}, function () {
window.location.href = data.url;
layer.closeAll();
})
} else {
layer.msg(payFailedText, {icon: 2, time: 2000})
layer.closeAll();
}
} catch (error) {
layer.msg(payFailedText, {icon: 2, time: 2000})
layer.closeAll();
}
}
SquarePaymentFlow();
</script>
</body>
</html>

View File

@@ -0,0 +1,242 @@
{include file="public/head" /}
<style>
body.user-page {
min-height: 100vh;
display: flex;
flex-direction: column;
background: #f5f7fb;
}
.user-main {
flex: 1;
padding: 32px 0 52px;
}
.account-layout {
display: grid;
grid-template-columns: 240px minmax(0, 1fr);
gap: 18px;
align-items: start;
}
.account-nav,
.account-panel {
background: #fff;
border: 1px solid rgba(15, 23, 42, .08);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(15, 23, 42, .06);
overflow: hidden;
}
.account-nav__title,
.account-panel__title {
background: var(--main-color);
color: #fff;
font-size: 17px;
font-weight: 800;
line-height: 50px;
padding: 0 20px;
}
.account-nav__link {
display: flex;
align-items: center;
gap: 9px;
height: 50px;
padding: 0 18px;
color: #263241;
border-bottom: 1px solid #eef1f5;
text-decoration: none;
font-size: 15px;
}
.account-nav__link:hover,
.account-nav__link.is-active {
color: var(--main-color);
background: rgba(21, 56, 84, .06);
text-decoration: none;
}
.account-panel__body {
padding: 18px;
}
.order-list {
display: grid;
gap: 14px;
}
.order-card {
border: 1px solid #dfe5ee;
border-radius: 8px;
overflow: hidden;
background: #fff;
}
.order-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.order-item {
display: grid;
grid-template-columns: 96px minmax(0, 1fr);
gap: 8px;
padding: 12px 14px;
border-bottom: 1px solid #edf1f5;
color: #334155;
line-height: 1.55;
min-width: 0;
}
.order-item:nth-child(odd) {
border-right: 1px solid #edf1f5;
}
.order-item strong {
color: #0f172a;
font-weight: 700;
white-space: nowrap;
}
.order-item span {
word-break: break-word;
}
.order-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 12px 14px;
background: #f8fafc;
}
.account-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 92px;
height: 36px;
padding: 0 16px;
border-radius: 4px;
background: var(--main-color);
color: #fff;
text-decoration: none;
font-weight: 700;
}
.account-btn:hover {
color: #fff;
background: #0f2b42;
text-decoration: none;
}
.empty-state {
padding: 52px 20px;
text-align: center;
color: #64748b;
}
.empty-state .account-btn {
margin-top: 16px;
}
@media (max-width: 768px) {
.user-main {
padding: 20px 12px 36px;
}
.account-layout {
grid-template-columns: 1fr;
gap: 12px;
}
.account-nav {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.account-nav__title {
display: none;
}
.account-nav__link {
justify-content: center;
padding: 0 8px;
font-size: 14px;
border-right: 1px solid #eef1f5;
}
.order-grid {
grid-template-columns: 1fr;
}
.order-item {
grid-template-columns: 88px minmax(0, 1fr);
}
.order-item:nth-child(odd) {
border-right: 0;
}
}
</style>
<body class="user-page">
{include file="public/newnav" /}
<main class="user-main">
<div class="layui-container">
<div class="account-layout">
<aside class="account-nav">
<div class="account-nav__title">个人中心</div>
<a href="{:url('user/index')}" class="account-nav__link is-active">
<i class="bi bi-receipt"></i><span>我的订单</span>
</a>
<a href="{:url('user/invoice')}" class="account-nav__link">
<i class="bi bi-file-earmark-text"></i><span>自助开票</span>
</a>
<a href="{:url('visa/index')}" class="account-nav__link">
<i class="bi bi-send"></i><span>签证申请</span>
</a>
</aside>
<section class="account-panel">
<div class="account-panel__title">我的订单</div>
<div class="account-panel__body">
<div class="order-list">
{volist name="order_list" id="vo"}
<article class="order-card">
<div class="order-grid">
<div class="order-item"><strong>订单号</strong><span>{$vo.order_sn}</span></div>
<div class="order-item"><strong>姓名</strong><span>{$vo.last_name} {$vo.first_name}</span></div>
<div class="order-item"><strong>出生日期</strong><span>{$vo.birth_date}</span></div>
<div class="order-item"><strong>护照号</strong><span>{$vo.passport_number}</span></div>
<div class="order-item"><strong>手机号</strong><span>{$vo.phone}</span></div>
<div class="order-item"><strong>下单时间</strong><span>{$vo.reg_time}</span></div>
<div class="order-item"><strong>状态</strong><span>{$vo.order_text}</span></div>
</div>
<div class="order-actions">
{eq name="$vo.pay_status" value="1"}
{eq name="$vo.invoice" value="0"}
<a class="account-btn" href="{:url('invoice/index',array('order_sn'=>$vo['order_sn']))}">开发票</a>
{/eq}
{/eq}
</div>
</article>
{/volist}
</div>
{empty name="order_list"}
<div class="empty-state">
<div>暂无订单记录</div>
<a class="account-btn" href="{:url('visa/index')}">去登记</a>
</div>
{/empty}
</div>
</section>
</div>
</div>
</main>
{include file="public/newfooter" /}
</body>
</html>

View File

@@ -0,0 +1,273 @@
{include file="public/head" /}
<style>
body.user-page {
min-height: 100vh;
display: flex;
flex-direction: column;
background: #f5f7fb;
}
.user-main {
flex: 1;
padding: 32px 0 52px;
}
.account-layout {
display: grid;
grid-template-columns: 240px minmax(0, 1fr);
gap: 18px;
align-items: start;
}
.account-nav,
.account-panel {
background: #fff;
border: 1px solid rgba(15, 23, 42, .08);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(15, 23, 42, .06);
overflow: hidden;
}
.account-nav__title,
.account-panel__title {
background: var(--main-color);
color: #fff;
font-size: 17px;
font-weight: 800;
line-height: 50px;
padding: 0 20px;
}
.account-nav__link {
display: flex;
align-items: center;
gap: 9px;
height: 50px;
padding: 0 18px;
color: #263241;
border-bottom: 1px solid #eef1f5;
text-decoration: none;
font-size: 15px;
}
.account-nav__link:hover,
.account-nav__link.is-active {
color: var(--main-color);
background: rgba(21, 56, 84, .06);
text-decoration: none;
}
.account-panel__body {
padding: 22px;
}
.invoice-tip {
margin: 0 0 20px;
padding: 12px 14px;
border: 1px solid rgba(149, 30, 20, .16);
border-radius: 6px;
color: #951e14;
background: rgba(149, 30, 20, .05);
line-height: 1.7;
text-align: center;
}
.invoice-tip a {
color: var(--main-color);
font-weight: 800;
text-decoration: none;
}
.invoice-form .layui-form-label {
display: block;
float: none;
width: auto;
padding: 0 0 7px;
color: #1f2937;
text-align: left;
font-weight: 800;
}
.invoice-form .layui-form-label span {
color: #e53935;
margin-left: 3px;
}
.invoice-form .layui-input-block {
margin-left: 0;
min-height: auto;
}
.invoice-form .layui-input {
height: 50px;
border: 1px solid #d8dee8;
border-radius: 4px;
font-size: 15px;
}
.invoice-form .layui-input:focus {
border-color: var(--main-color) !important;
box-shadow: 0 0 0 3px rgba(21, 56, 84, .08);
}
.invoice-form .layui-btn {
height: 50px;
line-height: 50px;
border-radius: 6px;
background: var(--main-color);
font-size: 17px;
font-weight: 800;
}
@media (max-width: 768px) {
.user-main {
padding: 20px 12px 36px;
}
.account-layout {
grid-template-columns: 1fr;
gap: 12px;
}
.account-nav {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.account-nav__title {
display: none;
}
.account-nav__link {
justify-content: center;
padding: 0 8px;
font-size: 14px;
border-right: 1px solid #eef1f5;
}
.account-panel__body {
padding: 18px;
}
}
</style>
<body class="user-page">
{include file="public/newnav" /}
<main class="user-main">
<div class="layui-container">
<div class="account-layout">
<aside class="account-nav">
<div class="account-nav__title">个人中心</div>
<a href="{:url('user/index')}" class="account-nav__link">
<i class="bi bi-receipt"></i><span>我的订单</span>
</a>
<a href="{:url('user/invoice')}" class="account-nav__link is-active">
<i class="bi bi-file-earmark-text"></i><span>自助开票</span>
</a>
<a href="{:url('user/multiple')}" class="account-nav__link">
<i class="bi bi-files"></i><span>合并开票</span>
</a>
</aside>
<section class="account-panel">
<div class="account-panel__title">自助开票</div>
<div class="account-panel__body">
<p class="invoice-tip">
适用于单个订单开票。多个订单请使用
<a href="{:url('user/multiple')}">合并开票</a>
</p>
<form class="layui-form invoice-form" id="lookupform" name="form1" autocomplete="off">
<div class="layui-row layui-col-space24">
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="passport_number">护照号码<span>*</span></label>
<div class="layui-input-block">
<input type="text" id="passport_number" name="passport_number"
maxlength="9" minlength="9" lay-verify="required"
placeholder="输入护照号码" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="order_sn">订单号<span>*</span></label>
<div class="layui-input-block">
<input type="text" id="order_sn" name="order_sn" lay-verify="required"
placeholder="输入订单号" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="last_name">姓氏<span>*</span></label>
<div class="layui-input-block">
<input type="text" id="last_name" name="last_name" maxlength="45"
lay-verify="required|englishName" placeholder="填写姓氏拼音"
class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label" for="first_name">名字<span>*</span></label>
<div class="layui-input-block">
<input type="text" id="first_name" name="first_name" maxlength="40"
lay-verify="required|englishName" placeholder="填写名字拼音"
class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12">
<button class="layui-btn layui-btn-fluid" lay-submit lay-filter="invoiceSubmit">提交</button>
</div>
</div>
</form>
</div>
</section>
</div>
</div>
</main>
{include file="public/newfooter" /}
<script>
layui.use(function () {
var $ = layui.$;
var form = layui.form;
var layer = layui.layer;
form.verify({
englishName: [/^[A-Za-z]+$/, '请填写英文拼音']
});
form.on('submit(invoiceSubmit)', function () {
var index1;
$.ajax({
type: 'POST',
url: "{:url('user/checkInvoice')}",
data: $('#lookupform').serialize(),
beforeSend: function () {
index1 = layer.load(1, {shade: [0.3, '#fff']});
},
success: function (data) {
layer.close(index1);
if (data.status == 1) {
layer.msg('查询成功');
window.location.href = data.url;
} else {
layer.msg(data.msg);
}
},
error: function () {
layer.close(index1);
layer.msg('请求失败,请稍后重试');
}
});
return false;
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,368 @@
{include file="public/head" /}
<style>
body.login-page {
min-height: 100vh;
display: flex;
flex-direction: column;
background: #f5f7fb;
}
.login-main {
flex: 1;
padding: 42px 0 56px;
}
.login-card {
max-width: 520px;
margin: 0 auto;
background: #fff;
border: 1px solid rgba(15, 23, 42, .08);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(15, 23, 42, .08);
overflow: hidden;
}
.login-card__hd {
background: var(--main-color);
color: #fff;
padding: 18px 24px;
}
.login-card__hd h1 {
margin: 0;
font-size: 22px;
font-weight: 800;
line-height: 1.35;
letter-spacing: 0;
}
.login-card__hd p {
margin: 6px 0 0;
color: rgba(255, 255, 255, .76);
font-size: 13px;
line-height: 1.6;
}
.login-form {
padding: 28px 24px 26px;
}
.login-form .layui-form-item {
margin-bottom: 18px;
}
.login-form .layui-input-wrap {
width: 100%;
}
.login-form .layui-input {
height: 50px;
border: 1px solid #d8dee8;
border-radius: 4px;
font-size: 15px;
}
.login-form .layui-input:focus {
border-color: var(--main-color) !important;
box-shadow: 0 0 0 3px rgba(21, 56, 84, .08);
}
.login-code-row {
display: grid;
grid-template-columns: minmax(0, 1fr) 148px;
gap: 12px;
}
.login-get {
width: 100%;
height: 50px;
line-height: 50px;
padding: 0 12px;
border-radius: 4px;
border-color: var(--main-color);
color: var(--main-color);
background: #fff;
font-weight: 700;
}
.login-get:hover,
.login-get:focus {
border-color: var(--main-color);
color: var(--main-color);
background: rgba(21, 56, 84, .06);
}
.login-get[disabled] {
border-color: #d8dee8 !important;
color: #9aa3af !important;
background: #f6f7f9 !important;
cursor: not-allowed;
}
.login-submit {
height: 50px;
line-height: 50px;
border-radius: 6px;
background: var(--main-color);
border-color: var(--main-color);
font-size: 17px;
font-weight: 800;
}
.login-submit:hover,
.login-submit:focus {
background: #0f2b42;
border-color: #0f2b42;
}
.login-agreement {
margin-top: 2px;
color: #475569;
font-size: 13px;
line-height: 1.7;
}
.login-agreement a {
color: var(--main-color);
font-weight: 700;
text-decoration: none;
}
.login-agreement a:hover {
text-decoration: underline;
}
.layui-form-checked[lay-skin=primary] > i {
border-color: var(--main-color) !important;
background-color: var(--main-color);
color: #fff;
}
.login-terms {
padding: 20px;
max-height: 70vh;
overflow-y: auto;
color: #333;
line-height: 1.8;
}
.login-terms h3 {
margin: 10px 0 8px;
color: var(--main-color);
font-size: 18px;
font-weight: 700;
}
.login-terms p {
margin: 0 0 10px;
font-size: 13px;
}
@media (max-width: 768px) {
.login-main {
padding: 24px 12px 36px;
}
.login-card__hd,
.login-form {
padding-left: 18px;
padding-right: 18px;
}
.login-code-row {
grid-template-columns: 1fr;
gap: 10px;
}
}
</style>
<body class="login-page">
{include file="public/newnav" /}
<main class="login-main">
<div class="layui-container">
<section class="login-card">
<div class="login-card__hd">
<h1>用户登录</h1>
<p>使用手机号验证码登录后,可查看订单进度和已提交的申请信息。</p>
</div>
<form class="layui-form login-form" autocomplete="off">
<div class="layui-form-item">
<div class="login-code-row">
<div class="layui-input-wrap">
<div class="layui-input-prefix">
<i class="layui-icon layui-icon-cellphone"></i>
</div>
<input type="text" name="phone" value="" lay-verify="required|phone"
placeholder="{:lang('visa.phone_placeholder')}"
lay-reqtext="{:lang('visa.phone_placeholder')}" autocomplete="off"
class="layui-input" id="reg-cellphone">
</div>
<input class="login-get layui-btn layui-btn-primary" type="button"
id="getcode" value="获取验证码" lay-on="reg-get-vercode">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-wrap">
<div class="layui-input-prefix">
<i class="layui-icon layui-icon-vercode"></i>
</div>
<input type="text" name="vercode" value="" lay-verify="required"
placeholder="{:lang('contact.yzm')}" lay-reqtext="请填写验证码"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item login-agreement">
<input type="checkbox" lay-filter="termsCheckbox" name="agreement" lay-skin="primary"
title="{:lang('visa.terms_acceptance')}" checked
lay-verify="required|checkboxRequired">
</div>
<div class="layui-form-item" style="margin-bottom: 0;">
<button class="layui-btn layui-btn-fluid login-submit" lay-submit lay-filter="demo-reg">登录</button>
</div>
</form>
</section>
</div>
</main>
{include file="public/newfooter" /}
<script>
layui.use(function () {
var $ = layui.$;
var form = layui.form;
var layer = layui.layer;
var util = layui.util;
var wait = 60;
var timer = null;
form.verify({
checkboxRequired: function () {
if (!$('input[name="agreement"]').prop('checked')) {
return '请先阅读并接受《条款与条件》及《隐私政策》';
}
}
});
function getLayerArea() {
return window.innerWidth <= 768 ? ['90%', '90%'] : ['50%', '60%'];
}
function openTermsDialog() {
layer.open({
type: 1,
title: '{:lang(\'js.terms_privacy_title\')}',
area: getLayerArea(),
btn: ['关闭'],
content: `
<div class="login-terms">
<h3>{:lang('js.terms_conditions')}</h3>
<p>{:str_replace('%country%', $country ?? '', lang('js.terms_intro'))}</p>
<p>{:str_replace('%country%', $country ?? '', lang('js.our_services'))}</p>
<p>{:str_replace('%country%', $country ?? '', lang('js.application_process'))}</p>
<p>{:lang('js.read_confirmation')}</p>
<h3>{:lang('js.privacy_policy')}</h3>
<p>{:lang('js.privacy_intro')}</p>
<h3>1. {:lang('js.scope')}</h3>
<p>{:lang('js.scope_content')}</p>
<h3>2. {:lang('js.information_use')}</h3>
<p>{:lang('js.information_use_content')}</p>
<h3>3. {:lang('js.information_disclosure')}</h3>
<p>{:lang('js.information_disclosure_content')}</p>
<h3>4. {:lang('js.information_storage')}</h3>
<p>{:lang('js.information_storage_content')}</p>
<h3>5. {:lang('js.cookie_use')}</h3>
<p>{:lang('js.cookie_use_content')}</p>
<h3>6. {:lang('js.information_security')}</h3>
<p>{:lang('js.information_security_content')}</p>
<h3>7. {:lang('js.policy_changes')}</h3>
<p>{:lang('js.policy_changes_content')}</p>
</div>
`
});
}
form.on('checkbox(termsCheckbox)', function (data) {
if (data.elem.checked) {
openTermsDialog();
}
});
form.on('submit(demo-reg)', function (data) {
if (!data.field.agreement) {
layer.msg('请先阅读并接受《条款与条件》及《隐私政策》', {icon: 2});
return false;
}
$.ajax({
url: '/home/login/login',
type: 'post',
data: data.field,
success: function (res) {
if (res.code == 1) {
layer.msg('登录成功', {icon: 1}, function () {
window.location.href = res.url;
});
} else {
layer.msg(res.msg, {icon: 2});
}
},
error: function () {
layer.msg('请求失败,请稍后重试', {icon: 2});
}
});
return false;
});
function settime() {
var $code = $('#getcode');
if (wait <= 0) {
$code.removeAttr('disabled').val('获取验证码');
wait = 60;
timer = null;
return;
}
$code.prop('disabled', true).val('重新发送 ' + wait + 's');
wait--;
timer = setTimeout(settime, 1000);
}
util.on('lay-on', {
'reg-get-vercode': function () {
var isvalid = form.validate('#reg-cellphone');
if (isvalid) {
$.ajax({
url: '/home/login/login_code',
type: 'POST',
data: {mobile: $('#reg-cellphone').val()},
dataType: 'json',
success: function (data) {
if (data.code == 1) {
if (timer) {
clearTimeout(timer);
}
wait = 60;
settime();
} else {
layer.msg(data.msg, {icon: 2});
}
},
error: function () {
layer.msg('验证码发送失败,请稍后重试', {icon: 2});
}
});
}
}
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,387 @@
{include file="public/head" /}
<style>
body.user-page {
min-height: 100vh;
display: flex;
flex-direction: column;
background: #f5f7fb;
}
.user-main {
flex: 1;
padding: 32px 0 52px;
}
.account-layout {
display: grid;
grid-template-columns: 240px minmax(0, 1fr);
gap: 18px;
align-items: start;
}
.account-nav,
.account-panel {
background: #fff;
border: 1px solid rgba(15, 23, 42, .08);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(15, 23, 42, .06);
overflow: hidden;
}
.account-nav__title,
.account-panel__title {
background: var(--main-color);
color: #fff;
font-size: 17px;
font-weight: 800;
line-height: 50px;
padding: 0 20px;
}
.account-nav__link {
display: flex;
align-items: center;
gap: 9px;
height: 50px;
padding: 0 18px;
color: #263241;
border-bottom: 1px solid #eef1f5;
text-decoration: none;
font-size: 15px;
}
.account-nav__link:hover,
.account-nav__link.is-active {
color: var(--main-color);
background: rgba(21, 56, 84, .06);
text-decoration: none;
}
.account-panel__body {
padding: 22px;
}
.invoice-tip {
margin: 0 0 20px;
padding: 12px 14px;
border: 1px solid rgba(149, 30, 20, .16);
border-radius: 6px;
color: #951e14;
background: rgba(149, 30, 20, .05);
line-height: 1.7;
text-align: center;
}
.invoice-tip a {
color: var(--main-color);
font-weight: 800;
text-decoration: none;
}
.invoice-row {
position: relative;
margin-bottom: 16px;
padding: 18px;
border: 1px solid #dfe5ee;
border-radius: 8px;
background: #fff;
}
.invoice-row__actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 4px;
}
.invoice-action {
display: inline-flex;
align-items: center;
gap: 5px;
color: var(--main-color);
cursor: pointer;
font-weight: 800;
user-select: none;
}
.invoice-action.is-danger {
color: #951e14;
}
.invoice-form .layui-form-label {
display: block;
float: none;
width: auto;
padding: 0 0 7px;
color: #1f2937;
text-align: left;
font-weight: 800;
}
.invoice-form .layui-form-label span {
color: #e53935;
margin-left: 3px;
}
.invoice-form .layui-input-block {
margin-left: 0;
min-height: auto;
}
.invoice-form .layui-input {
height: 50px;
border: 1px solid #d8dee8;
border-radius: 4px;
font-size: 15px;
}
.invoice-form .layui-input:focus {
border-color: var(--main-color) !important;
box-shadow: 0 0 0 3px rgba(21, 56, 84, .08);
}
.invoice-submit {
height: 50px;
line-height: 50px;
border-radius: 6px;
background: var(--main-color);
font-size: 17px;
font-weight: 800;
}
@media (max-width: 768px) {
.user-main {
padding: 20px 12px 36px;
}
.account-layout {
grid-template-columns: 1fr;
gap: 12px;
}
.account-nav {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.account-nav__title {
display: none;
}
.account-nav__link {
justify-content: center;
padding: 0 8px;
font-size: 14px;
border-right: 1px solid #eef1f5;
}
.account-panel__body {
padding: 18px;
}
}
</style>
<body class="user-page">
{include file="public/newnav" /}
<main class="user-main">
<div class="layui-container">
<div class="account-layout">
<aside class="account-nav">
<div class="account-nav__title">个人中心</div>
<a href="{:url('user/index')}" class="account-nav__link">
<i class="bi bi-receipt"></i><span>我的订单</span>
</a>
<a href="{:url('user/invoice')}" class="account-nav__link">
<i class="bi bi-file-earmark-text"></i><span>自助开票</span>
</a>
<a href="{:url('user/multiple')}" class="account-nav__link is-active">
<i class="bi bi-files"></i><span>合并开票</span>
</a>
</aside>
<section class="account-panel">
<div class="account-panel__title">合并开票</div>
<div class="account-panel__body">
<p class="invoice-tip">
适用于多个订单合并开票。单个订单请使用
<a href="{:url('user/invoice')}">自助开票</a>
</p>
<form class="layui-form invoice-form" id="lookupform" name="form1" autocomplete="off">
<div id="invoiceRows">
<div class="invoice-row">
<div class="layui-row layui-col-space24">
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">护照号码<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="passport_number" maxlength="9" minlength="9"
lay-verify="required" placeholder="输入护照号码" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">订单号<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="order_sn" lay-verify="required"
placeholder="输入订单号" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">姓氏<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="last_name" maxlength="45"
lay-verify="required|englishName" placeholder="填写姓氏拼音"
class="layui-input">
</div>
</div>
</div>
<div class="layui-col-xs12 layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">名字<span>*</span></label>
<div class="layui-input-block">
<input type="text" name="first_name" maxlength="40"
lay-verify="required|englishName" placeholder="填写名字拼音"
class="layui-input">
</div>
</div>
</div>
</div>
<div class="invoice-row__actions">
<span class="invoice-action js-add-row"><i class="layui-icon layui-icon-add-circle"></i>添加</span>
<span class="invoice-action is-danger js-remove-row"><i class="layui-icon layui-icon-reduce-circle"></i>删除</span>
</div>
</div>
</div>
<button class="layui-btn layui-btn-fluid invoice-submit" lay-submit lay-filter="multipleSubmit">提交</button>
</form>
</div>
</section>
</div>
</div>
</main>
{include file="public/newfooter" /}
<script>
layui.use(function () {
var $ = layui.$;
var form = layui.form;
var layer = layui.layer;
form.verify({
englishName: [/^[A-Za-z]+$/, '请填写英文拼音']
});
function refreshRemoveButtons() {
var $rows = $('#invoiceRows .invoice-row');
$rows.find('.js-remove-row').toggle($rows.length > 1);
}
function getRowData($row) {
return {
passport_number: $.trim($row.find("input[name='passport_number']").val()),
order_sn: $.trim($row.find("input[name='order_sn']").val()),
last_name: $.trim($row.find("input[name='last_name']").val()),
first_name: $.trim($row.find("input[name='first_name']").val())
};
}
function validateRow($row) {
var data = getRowData($row);
if (!data.passport_number) {
layer.msg('请输入护照号');
return false;
}
if (!data.order_sn) {
layer.msg('请输入订单号');
return false;
}
if (!data.last_name) {
layer.msg('请输入姓氏');
return false;
}
if (!data.first_name) {
layer.msg('请输入名字');
return false;
}
if (!/^[A-Za-z]+$/.test(data.last_name) || !/^[A-Za-z]+$/.test(data.first_name)) {
layer.msg('请填写英文拼音');
return false;
}
return true;
}
$(document).on('click', '.js-add-row', function () {
var $row = $(this).closest('.invoice-row');
if (!validateRow($row)) {
return false;
}
var $newRow = $row.clone(false, false);
$newRow.find('input').val('');
$('#invoiceRows').append($newRow);
refreshRemoveButtons();
});
$(document).on('click', '.js-remove-row', function () {
$(this).closest('.invoice-row').remove();
refreshRemoveButtons();
});
form.on('submit(multipleSubmit)', function () {
var datas = [];
var isValid = true;
var index1;
$('#invoiceRows .invoice-row').each(function () {
var $row = $(this);
if (!validateRow($row)) {
isValid = false;
return false;
}
datas.push(getRowData($row));
});
if (!isValid) {
return false;
}
$.ajax({
type: 'POST',
url: "{:url('user/checkMultiple')}",
data: {datas: JSON.stringify(datas)},
beforeSend: function () {
index1 = layer.load(1, {shade: [0.3, '#fff']});
},
success: function (data) {
layer.close(index1);
if (data.status == 1) {
layer.msg('查询成功');
window.location.href = data.url;
} else {
layer.msg(data.msg);
}
},
error: function () {
layer.close(index1);
layer.msg('请求失败,请稍后重试');
}
});
return false;
});
refreshRemoveButtons();
});
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
{include file="public/head"}
<link rel="stylesheet" href="/static/css/wepay.css?v=1">
<body class="wepay-page">
<div class="zh_content">
{include file="public/newnav"}
</div>
<main class="layui-container wepay-shell">
<section class="wepay-card">
<div class="wepay-card__head">
<div>
<p class="wepay-eyebrow">{:lang('wepay.payment_title')}</p>
<h1>{:lang('wepay.order_success_notice')}</h1>
</div>
<img src="/static/image/zf3.png" alt="{:lang('wepay.payment_title')}">
</div>
<div class="wepay-summary">
<div>
<span>{:lang('wepay.order_number')}</span>
<strong>{$order_sn}</strong>
</div>
<div>
<span>{:lang('wepay.amount_due')}</span>
<strong class="wepay-amount">¥{$total_fee}</strong>
</div>
</div>
<div class="wepay-methods">
<div class="wepay-method">
<div class="wepay-method__title">
<img src="/static/image/wx.png" alt="{:lang('wepay.wechat_pay')}">
<span>{:lang('wepay.wechat_pay')}</span>
</div>
<div class="wepay-qr">
<img src="{$url}" alt="{:lang('wepay.wechat_pay')}">
</div>
</div>
<div class="wepay-method">
<div class="wepay-method__title">
<img src="/static/image/zfb.jpg" alt="{:lang('wepay.alipay')}">
<span>{:lang('wepay.alipay')}</span>
</div>
<div class="wepay-qr">
<img src="{$aliurl}" alt="{:lang('wepay.alipay')}">
</div>
</div>
<aside class="wepay-guide">
<img src="/static/image/saoyisao.png" alt="{:lang('wepay.scan_to_pay')}">
<h2>{:lang('wepay.scan_to_pay')}</h2>
<p>{:lang('wepay.scan_notice')}</p>
<div class="wepay-waiting">{:lang('wepay.processing')}</div>
</aside>
</div>
</section>
</main>
{include file="public/newfooter" }
<script>
var interval = setInterval(function () {
layui.$.ajax({
type: "POST",
url: "{:url('wepay/pcresult', ['order_sn' => $order_sn])}",
dataType: "json",
success: function (data) {
if (data.status == 1) {
clearInterval(interval);
location.href = data.url;
}
}
});
}, 1000);
</script>
</body>
</html>

View File

@@ -0,0 +1,154 @@
{include file="public/head"}
<link rel="stylesheet" href="/static/css/wepay.css?v=2">
</head>
<body class="wepay-page">
<div class="zh_content">
{include file="public/newnav" }
</div>
<main
class="container wepay-shell"
id="payment-success-page"
data-payment-confirmed="{$payment_confirmed}"
data-poll-status-url="{$poll_status_url}"
>
<section class="wepay-result-card">
<div id="payment-pending-block" class="payment-pending" style="display: none;">
<div class="wepay-result-icon wepay-result-icon--pending">
<span class="payment-spinner"></span>
</div>
<p class="wepay-eyebrow">{:lang('wepay.payment_title')}</p>
<h1 class="payment-pending-title">{:lang('sqpay.notice_title')}</h1>
<p class="payment-pending-text">{:lang('sqpay.notice_text')}</p>
<p class="payment-pending-order">
{:str_replace('%order_sn%', $order_sn ?? '', lang('pay.order_submitted'))}
</p>
<p class="payment-pending-status" id="payment-pending-status">{:lang('sqpay.notice_text')}</p>
<div class="wepay-actions">
<a class="wepay-button wepay-button--ghost" href="{$repay_url}">{:lang('pay.repay_button')}</a>
</div>
</div>
<div id="payment-success-block" class="wepay-success-detail">
<div class="wepay-result-icon wepay-result-icon--success">&check;</div>
<p class="wepay-eyebrow">{:lang('wepay.payment_success')}</p>
<h1>{:lang('pay.thank_you_message')}</h1>
<p class="wepay-success-order">
{:str_replace('%order_sn%', $order_sn ?? '', lang('pay.order_submitted'))}
</p>
<p class="wepay-success-text">
{:str_replace('%country%', $country ?? '', lang('pay.processing_time_info'))}
</p>
{if $arrive_date_info}
<p class="wepay-success-text">
{$arrive_date_info}
</p>
{/if}
<p class="wepay-contact-text">
{:lang('pay.contact')}
</p>
</div>
<div class="wepay-actions" id="payment-success-action">
<a class="wepay-button" href="{:url('visa/index')}">{:lang('pay.visa_application_button')}</a>
</div>
</section>
</main>
{include file="public/newfooter" }
<script>
(function () {
var pageEl = document.getElementById('payment-success-page');
if (!pageEl) {
return;
}
var isConfirmed = pageEl.getAttribute('data-payment-confirmed') === '1';
var pollUrl = pageEl.getAttribute('data-poll-status-url');
var pendingBlock = document.getElementById('payment-pending-block');
var successBlock = document.getElementById('payment-success-block');
var successAction = document.getElementById('payment-success-action');
var pendingStatus = document.getElementById('payment-pending-status');
var pendingText = "{:lang('sqpay.notice_text')}";
var successText = "{:lang('controller.payment_success')}";
var pollTimer = null;
var maxAttempts = 30;
var attempt = 0;
function showPending() {
if (pendingBlock) {
pendingBlock.style.display = 'block';
}
if (successBlock) {
successBlock.style.display = 'none';
}
if (successAction) {
successAction.style.display = 'none';
}
}
function showSuccess() {
if (pendingBlock) {
pendingBlock.style.display = 'none';
}
if (successBlock) {
successBlock.style.display = 'block';
}
if (successAction) {
successAction.style.display = 'block';
}
}
if (isConfirmed || !pollUrl) {
showSuccess();
return;
}
showPending();
async function pollStatus() {
attempt++;
try {
var response = await fetch(pollUrl, {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
var data = await response.json();
if (data.status === 1) {
if (pendingStatus) {
pendingStatus.textContent = successText;
}
window.clearInterval(pollTimer);
showSuccess();
return;
}
if (data.status === -1 && data.url) {
window.clearInterval(pollTimer);
window.location.href = data.url;
return;
}
if (pendingStatus) {
pendingStatus.textContent = data.msg || pendingText;
}
} catch (e) {
if (pendingStatus) {
pendingStatus.textContent = pendingText;
}
}
if (attempt >= maxAttempts) {
window.clearInterval(pollTimer);
if (pendingStatus) {
pendingStatus.textContent = pendingText;
}
}
}
pollStatus();
pollTimer = window.setInterval(pollStatus, 2000);
})();
</script>
</body>
</html>

725
app/lang/en-us.php Normal file
View File

@@ -0,0 +1,725 @@
<?php
return [
'contact' => [
'info' => 'If you have any questions about your application, please fill out the form below. Our customer service team will respond within 24 hours.',
'full_name' => 'Full Name',
'email' => 'Email Address',
'repeat_email' => 'Confirm Email Address',
'passport_number' => 'Passport Number',
'is_apply' => 'Have you already submitted an application through us?',
'contact_reason' => 'Reason for Contact',
'order_sn' => 'Order Number',
'comment' => 'Message',
'yzm' => 'Verification Code',
'change' => 'Change',
'menu_contact' => 'Contact Us',
'yzm_error' => 'Incorrect verification code',
'email_error' => 'Invalid email address',
'yes' => 'Yes',
'no' => 'No',
'submit_now' => 'Submit Now',
'optional' => '(Optional)',
'default_reqtext' => 'Required field cannot be empty',
'success' => 'Success',
'fail' => 'Failed',
],
'wepay' => [
'payment_title' => 'Order Payment',
'order_success_notice' => 'Your order has been submitted. Please complete payment as soon as possible.',
'order_number' => 'Order Number',
'amount_due' => 'Amount Due',
'currency_unit' => 'USD',
'wechat_pay' => 'WeChat Pay',
'alipay' => 'Alipay',
'scan_to_pay' => 'Scan with your mobile phone to complete payment',
'scan_notice' => 'After payment is completed, this page will redirect automatically. Please do not close or refresh it.',
'processing' => 'Waiting for payment result...',
'missing_order_sn' => 'Missing order number. Please fill in the application again.',
'system_error' => 'Unexpected error. Please submit again.',
'already_paid' => 'Payment has already been completed. Please do not pay again.',
'payment_success' => 'Payment successful',
'payment_not_completed' => 'Payment has not been completed. Please try again.',
'payment_failed_retry' => 'Payment failed. Please try again later.',
'order_missing_retry' => 'Order number does not exist. Please pay again.',
'pay_body' => 'Electronic visa registration service fee',
'success_title' => 'Thank you for your application',
'success_order_notice' => 'Your order has been submitted. Order number: ',
'success_desc' => 'We will process your application shortly. Please watch for email, SMS, or WeChat notifications.',
'success_tip_a' => 'If your travel date is close or you need expedited processing, please contact customer service as soon as possible.',
'success_tip_b' => 'Normally, your documents will be submitted to the relevant authority within the stated service time. Some applications may require additional review time.',
'apply_again' => 'Apply Again'
],
'head' => [
'title' => 'Thailand Arrival Card Chinese Registration System | Online Application & Registration',
'keywords' => 'Thailand Arrival Card, Thailand Arrival Card Chinese Website, Thailand Arrival Card Registration System, Thailand Arrival Card Online Application, Thailand Immigration Registration',
'description' => 'The Thailand Arrival Card Chinese Registration System provides a convenient online registration service, simplifying Thailand immigration procedures. Easy to use and efficient, helping you complete your arrival card application quickly.',
'og_title' => 'Thailand Arrival Card Chinese Registration System | Online Application & Registration',
'og_description' => 'Provides Thailand Arrival Card Chinese registration service, allowing you to efficiently complete the online application and registration of your arrival card.'
],
'index' => [
'notice' => 'Please note:',
'notice_text'=>'This website is an independently operated business service platform that, leveraging its self-developed intelligent system, offers three core services: the intelligent form-filling system efficiently simplifies the form completion process, precise OCR recognition quickly extracts text and image information, and intelligent AI translation supports accurate multilingual conversion. It focuses on providing professional paid services to users and is not affiliated with any government department.',
'home' => 'Home',
'online_application' => 'Online Application',
'entry_card_query' => 'Entry Card Query',
'usage_help' => 'News',
'news_author' => 'author',
'news_time' => 'time',
'visa_service' => 'Visa Service',
'welcome_message' => 'Welcome to %country% Entry Card Registration System',
'process_title' => 'Entry Registration Operation Guide',
'step1_title' => 'Scan passport information',
'step1_desc' => "Only the passport's main page is needed, automatically recognizing fields such as name, passport number, and date of birth (can be manually corrected).",
'step2_title' => 'Fill in the itinerary and submit the application',
'step2_desc' => 'Verify the consistency of travel purpose, duration of stay, and address/contact information in Singapore',
'step3_title' => 'Download registration file',
'step3_desc' => 'After the payment is completed, you can search for and download the file/confirmation page using the order number, which is convenient for check-in or immigration verification',
'entry_card_application' => 'Entry Card Application',
'tip'=>'Tip',
'tip_1'=>'The English translation should be natural (profession/organization type/travel purpose)',
'tip_2'=>'The itinerary is consistent with the domestic address.',
'tip_3'=>'If departing soon, you can choose the expedited lane (subject to the system).',
'tip_4'=>'It is recommended to save a PDF/screenshot for backup',
'application_description1' => 'All travelers visiting %country% must have a %country% entry card to enter the country. To obtain an electronic %country% entry card, eligible individuals need to complete a simple online application form.',
'application_notice' => 'Note: All travelers arriving in %country% must submit a health declaration form for immigration clearance at the border. The health declaration can be completed and submitted as part of the online entry card application service.',
'apply_now' => 'Apply Now √',
'electronic_arrival_card' => 'Electronic Arrival Card',
'environmental_initiative' => 'In response to environmental initiatives, the %country% government has fully implemented an electronic arrival card system, replacing traditional paper departure/embarkation cards.',
'digital_process_benefits' => 'The system automatically links passport information, digitizes the arrival process for visitor convenience, enables fully online applications, saves offline processing time, provides real-time arrival status updates, and improves clearance efficiency.',
'submission_requirement' => 'All travelers must submit the %country% arrival card within three (3) days before arrival (including the day of arrival), which can be completed online or at the airport upon arrival.',
'card_validity' => 'The %country% electronic arrival card is valid from your indicated arrival date and is only valid for a single entry.',
'why_choose_us_title' => 'Service Advantages',
'why_choose_us_subtitle' => 'Make registration clearer and easier',
'convenient' => 'Convenient',
'convenient_desc' => 'Chinese guidance: Validate key fields to reduce repeated modifications.',
'secure' => 'Secure',
'secure_desc' => 'Encrypted transmission, your private information is more secure.',
'approval' => 'Reduce returns',
'approval_desc' => 'Key points for experience reminders to reduce common errors.',
'support' => 'Online Support',
'support_desc' => 'If you encounter any problems, you can contact customer service for assistance.',
'clients' => 'Serving Clients',
'clients_desc' => 'We have served clients across multiple industries and fields, demonstrating our strong capabilities and client trust.',
'service' => 'Service Support',
'service_desc' => 'We provide 24/7 service support throughout the year, ready to respond to your needs at any time.',
'experience' => 'Years of Experience',
'experience_desc' => 'With 10 years of extensive industry experience, we have accumulated rich professional knowledge and practical capabilities.',
'approval_rate' => 'Visa Approval Rate',
'approval_rate_desc' => 'Our visa application approval rate remains at the leading level in the industry, thanks to our extensive experience and professional team.',
'setmeal'=>'Handling Packages and Timing',
'suitable_for_advance_preparation'=>'Suitable for advance preparation',
'processing_time_limit'=>'Processing time is subject to system display',
'chinese_guided_filling'=>'Chinese-guided filling',
'key_field_consistency_check'=>'Key field consistency check',
'basic_error_reminder'=>'Basic error reminder',
'suitable_for_departure_soon'=>'Suitable for departure soon',
'urgent_availability'=>'Urgent availability is subject to the system',
'priority_processing_queue'=>'Priority processing queue',
'stricter_reminder_for_key_fields'=>'Stricter reminder for key fields',
'faster_feedback_and_supplementary_suggestions'=>'Faster feedback and supplementary suggestions',
'group_travel_batch_reg'=>'Group travel / Batch registration',
'group_meta_info'=>'Support table import and dedicated personnel connection',
'batch_data_collect'=>'Batch data collection and sorting',
'unified_verify_submit'=>'Unified verification and submission',
'progress_track_reconcile'=>'Progress tracking and reconciliation',
'select_standard_plan'=>'Select Standard Plan',
'select_urgent_plan'=>'Select Urgent Plan',
'contact_team_plan'=>'Contact Team Plan',
'common_questions'=>'Common Questions',
'back_to_top'=>'Back to Top',
// FAQ related
'faq_is_official_website' => 'Is this the official website?',
'faq_is_official_website_answer' => 'No. This website is an independently operated business service platform that provides paid agency services and filling assistance, and is not affiliated with any government department.',
'faq_guarantee_approval' => 'Is approval guaranteed?',
'faq_guarantee_approval_answer' => 'Approval cannot be guaranteed. We provide information sorting, filling assistance, and error avoidance suggestions, and the final result is determined by the review of relevant institutions.',
'faq_query_download_after_payment' => 'How to query and download after payment?',
'faq_query_download_after_payment_answer' => 'Enter the "Immigration Card Query" page, input the order number to query, and download the confirmation page/file; if it cannot be found, you can contact customer service for assistance in verification.',
],
'visa' => [
'yellow_fever' => 'Do you have a vaccination certificate against yellow fever',
'yellow_fever_time' => 'Vaccination time',
'step1_title' => 'Submit Application Online',
'step2_title' => 'Check and Confirm Payment',
'step3_title' => 'Receive Approval Confirmation',
'verify'=>'Please carefully verify the information. Once confirmed, no modifications can be made',
'confirm_name' => 'Name',
'personal_details_title' => 'Personal Details',
'passport_number' => 'Passport Number',
'passport_number_placeholder' => 'Please enter or scan passport',
'upload_passport_title' => 'Take/upload valid passport photo',
'nationality' => 'Nationality',
'select_nationality_placeholder' => 'Select or search nationality',
'last_name_en' => 'Last Name (English)',
'last_name_placeholder' => 'Enter last name',
'first_name_en' => 'First Name (English)',
'first_name_placeholder' => 'Enter first name',
'middle_name' => 'Middle Name',
'optional' => 'Optional',
'middle_name_placeholder' => 'Enter middle name if applicable',
'birth_date' => 'Date of Birth',
'birth_date_placeholder' => 'Select date of birth',
'occupation' => 'Occupation',
'occupation_placeholder' => 'Enter your occupation',
'email_address' => 'Email Address',
'email_placeholder' => 'Enter email address',
'phone_number' => 'Phone Number',
'phone_number_placeholder' => 'Please enter phone number',
'residence_country' => 'Country of Residence',
'select_residence_country_placeholder' => 'Select or search country of residence',
'residence_city' => 'City of Residence',
'residence_city_placeholder' => 'Enter city of residence',
'visa_number' => 'Visa Number',
'visa_number_placeholder' => 'Enter visa number',
'gender' => 'Gender',
'male' => 'Male',
'female' => 'Female',
'unknown' => 'Unknown',
'next_step' => 'Next Step',
'travel_information_title' => 'Travel Information',
'arrival_date' => 'Arrival Date',
'arrival_date_placeholder' => 'Select arrival date',
'departure_date' => 'Departure Date',
'departure_date_placeholder' => 'Select departure date',
'departure_country' => 'Departure Country/Region',
'select_departure_country_placeholder' => 'Select or search departure country/region',
'travel_purpose' => 'Travel Purpose',
'select_travel_purpose_placeholder' => 'Select travel purpose',
'holiday' => 'Holiday/Tourism',
'meeting' => 'Meeting',
'sports' => 'Sports Event',
'business' => 'Business',
'incentive' => 'Incentive',
'medical' => 'Medical',
'education' => 'Education',
'convention' => 'Convention/Forum',
'employment' => 'Employment',
'exhibition' => 'Exhibition',
'other_purpose' => 'Other (Please Specify)',
'other_purpose_specify' => 'Other Travel Purpose',
'other_purpose_placeholder' => 'Enter other travel purpose',
'entry_transport' => 'Entry Transportation',
'air' => 'Air',
'land' => 'Land',
'sea' => 'Sea',
'transport_number' => 'Entry Flight/Ship/Transport Number',
'transport_number_placeholder' => 'Enter flight/ship/transport number',
'transport_mode' => 'Entry Transport Mode',
'out_transport_mode' => 'Exit Transport Mode',
'select_transport_mode' => 'Select transport mode',
'commercial_flight' => 'Commercial Flight',
'private_flight' => 'Private/Cargo Flight',
'other_transport' => 'Other (Please Specify)',
'other_transport_specify' => 'Other Transport Mode',
'other_transport_placeholder' => 'Enter other transport mode',
'car' => 'Car',
'train' => 'Train',
'cruise' => 'Cruise Ship',
'commercial_vessel' => 'Commercial Vessel',
'departure_transport' => 'Departure Transportation',
'departure_transport_number' => 'Exit Flight/Ship/Transport Number',
'departure_transport_placeholder' => 'Enter flight/ship/transport number',
'is_transit_passenger' => 'Is Transit Passenger',
'yes' => 'Yes',
'no' => 'No',
'accommodation_type' => 'Accommodation Type',
'select_accommodation_type' => 'Select accommodation type',
'hotel' => 'Hotel',
'youth_hostel' => 'Youth Hostel',
'guest_house' => 'Guest House',
'friends_place' => "Friend's Place",
'apartment' => 'Apartment',
'other_accommodation' => 'Other (Please Specify)',
'other_accommodation_specify' => 'Other Accommodation Type',
'other_accommodation_placeholder' => 'Enter other accommodation type',
'province' => 'Province',
'select_province' => 'Select province',
'city' => 'City',
'select_city' => 'Select city',
'district' => 'District',
'select_district' => 'Select district',
'address' => 'Address',
'address_placeholder' => 'Enter address',
'visited_countries' => 'Select countries/regions stayed within 2 weeks before arrival',
'applicant_declaration_title' => 'Applicant Declaration',
'declaration_statement' => 'I declare that the information provided in this application is true, complete and correct.',
'terms_acceptance' => 'I have read and understood the Terms and Conditions and Privacy Policy.',
'select_package_title' => 'Select Package',
'currency_unit' => 'USD',
'submit_application_button' => 'Submit Application',
'select_payment_method' => 'Select Payment Method',
'wechat_pay' => 'WeChat Pay',
'alipay' => 'Alipay',
'card' => 'Credit card',
],
'sqpay' => [
'card' => 'Credit card',
'payment_title' => 'Online Payment',
'total_amount_label' => 'Total amount to be paid',
'card_pay_title' => 'Card Payment',
'pay_now' => 'Pay Now',
'notice_title' => 'Notice',
'notice_text' => 'Payment may take a few minutes. Please do not click Back, Forward, or Refresh while processing.',
'pay_success' => 'Payment successful',
'pay_failed' => 'Payment failed',
'success_notice' => 'Your payment has been submitted successfully. We will process your application shortly and notify you by email.'
],
'lookup' => [
'status_query_title' => 'Status Query',
'passport_number' => 'Passport Number',
'passport_number_placeholder' => 'Enter passport number',
'upload_passport_title' => 'Take/upload valid passport photo',
'birth_date' => 'Date of Birth',
'birth_date_placeholder' => 'Select date of birth',
'last_name' => 'Last Name',
'last_name_placeholder' => 'Enter last name',
'last_name_tooltip' => 'Please enter your last name in English as shown on your passport',
'first_name' => 'First Name',
'first_name_placeholder' => 'Enter first name',
'first_name_tooltip' => 'Please enter your first name in English as shown on your passport',
'query_status_button' => 'Query Status',
'registered_status' => 'Registered',
'approval_message' => 'Your entry card has been approved and your application is complete.',
'field' => 'Field',
'value' => 'Value',
'document' => 'Document',
'status' => 'Status',
'in_progress' => 'In Progress',
'click_download' => 'Click to download',
'english_only' => 'English only'
],
'js' => [
'verify'=>'Please verify your information',
'confirm'=>'confirm',
'edit'=>'edit',
'data_load_failed' => 'Data load failed: ',
'request_error' => 'Request error, please try again later',
'select_city_placeholder' => 'Select city',
'select_district_placeholder' => 'Select district',
'select_package' => 'Please select a package',
'select_option_required' => 'Please select an option',
'alpha_numeric_only' => 'Letters and numbers only',
'select_country' => 'Please select country',
'alert_message' => 'When the application time exceeds 3 days, the order will enter reservation mode. When the application time reaches the specified range, the system will automatically apply for the entry card.',
'notice' => 'Notice',
'departure_date_error' => 'Departure date cannot be earlier than arrival date',
'check_declaration' => 'Please check the declaration',
'uppercase_only' => 'Please enter uppercase letters only',
'chinese_only' => 'Please enter Chinese characters only',
'max_100_chars' => 'Maximum 100 characters allowed',
'select_one_option' => 'Please select at least one option',
'english_only' => 'English letters only',
'field_required' => ' is required',
'upload_image_only' => 'File must be an image',
'browser_not_supported' => 'Your browser does not support upload',
'visa_unrecognized' => 'Unrecognized, please enter visa number manually',
'manual_entry_required' => 'Unrecognized, please enter manually',
'upload_retry' => 'Please upload again',
'terms_privacy_title' => 'Terms and Conditions & Privacy Policy',
'terms_conditions' => 'Terms and Conditions',
'terms_intro' => 'By browsing, accessing, and using this website, you understand and agree to the terms and conditions set forth herein, referred to as "Terms and Conditions" and "Privacy Policy". Electronic visa applicants who submit %country% e-visa applications through this website will be referred to as "Applicant", "User", "You". Terms such as "We", "Us", "Our", "This Website" directly refer to this website. It is important for you to know that everyone\'s legal interests are protected, and our relationship with you is built on trust. Please note that you must accept these terms of service to use our website and the services we provide.',
'our_services' => 'About Our Services Our services act as an online application service provider to facilitate the electronic visa process for foreign nationals visiting %country%. Our agents will assist you in obtaining your travel authorization from the %country% government, and we will then provide you with that authorization. Our services include properly reviewing all your information, assisting with application completion, and checking the accuracy, completeness, spelling, and grammar of the entire document. Additionally, to process requests, we may contact you via email or phone for additional information.',
'application_process' => 'After completing the application form provided on our website, your travel authorization document request will be submitted after expert review. Your e-visa application requires approval from the %country% government. However, if any details are entered incorrectly or incompletely, your application may be delayed or rejected. All information provided by you or designated third-party agents must be true and correct. If you or your agent intentionally submit any false, fictitious, or forged statements or representations when filling out information, resulting in the rejection of your %country% e-visa application, you will bear full responsibility. After completing the information, we will provide results according to the processing time of the package you selected, and information cannot be changed after submission.',
'read_confirmation' => 'Please indicate that you have read and understood the information provided above',
'privacy_policy' => 'Privacy Policy',
'privacy_intro' => 'This application respects and protects the personal privacy rights of all users who use the service. To provide you with more accurate and personalized services, this application will use and disclose your personal information in accordance with the provisions of this privacy policy. However, this application will treat this information with a high degree of diligence and duty of care. Except as otherwise provided in this privacy policy, this application will not disclose this information to the public or provide it to third parties without your prior permission. This application will update this privacy policy from time to time. When you agree to this application\'s service usage agreement, you are deemed to have agreed to the entire content of this privacy policy. This privacy policy is an integral part of this application\'s service usage agreement.',
'scope' => 'Scope of Application',
'scope_content' => '(a) Personal registration information you provide when registering for this application account according to this application\'s requirements; (b) Information automatically received and recorded by this application on your browser and computer when you use this application\'s network services or visit this application platform web pages, including but not limited to your IP address, browser type, language used, access date and time, software and hardware characteristic information, and web page records you need; (c) User personal data obtained by this application through legal means from business partners. You understand and agree that the following information is not subject to this privacy policy: (a) Keyword information you enter when using the search services provided by this application platform; (b) Information collected by this application about your published information on this application, including but not limited to participation in activities, transaction information, and evaluation details; (c) Violations of legal provisions or this application\'s rules and measures already taken by this application against you.',
'information_use' => 'Information Use',
'information_use_content' => '(a) This application will not provide, sell, rent, share, or trade your personal information to any unrelated third party, unless prior permission is obtained from you, or that third party and this application (including this application\'s affiliated companies) individually or jointly provide services to you, and after the service ends, they will be prohibited from accessing all such materials including those they were previously able to access. (b) This application also does not allow any third party to collect, edit, sell, or freely disseminate your personal information by any means. If any user of this application platform engages in the above activities, this application has the right to immediately terminate the service agreement with that user upon discovery. (c) For the purpose of serving users, this application may use your personal information to provide you with information you are interested in, including but not limited to sending you product and service information, or sharing information with this application\'s partners so they can send you information about their products and services (the latter requires your prior consent).',
'information_disclosure' => 'Information Disclosure',
'information_disclosure_content' => 'Under the following circumstances, this application will disclose your personal information in whole or in part according to your personal wishes or legal provisions: (a) Disclosure to third parties with your prior consent; (b) Sharing your personal information with third parties is necessary to provide the products and services you request; (c) Disclosure to third parties or administrative or judicial institutions in accordance with relevant legal provisions, or requirements of administrative or judicial institutions; (d) Disclosure to third parties is necessary when you violate relevant Chinese laws and regulations or this application\'s service agreement or related rules; (e) If you are a qualified intellectual property complainant and have filed a complaint, disclosure to the complained party at their request to facilitate the resolution of possible rights disputes; (f) In a transaction created on this application platform, if any party to the transaction performs or partially performs the transaction obligations and requests information disclosure, this application has the right to decide to provide the user with necessary information such as the contact details of the transaction counterparty to facilitate the completion of the transaction or resolution of disputes. (g) Other disclosures that this application considers appropriate according to laws, regulations, or website policies.',
'information_storage' => 'Information Storage and Exchange',
'information_storage_content' => 'The information and data about you collected by this application will be stored on servers of this application and/or its affiliated companies. This information and data may be transmitted to countries or regions outside your country of residence or where this application collects information and data, and may be accessed, stored, and displayed overseas.',
'cookie_use' => 'Use of Cookies',
'cookie_use_content' => '(a) If you do not refuse to accept cookies, this application will set or access cookies on your computer so that you can log in or use this application platform services or functions that rely on cookies. This application uses cookies to provide you with more thoughtful personalized services, including promotional services. (b) You have the right to choose to accept or refuse to accept cookies. You can refuse to accept cookies by modifying your browser settings. However, if you choose to refuse cookies, you may not be able to log in or use this application\'s network services or functions that rely on cookies. (c) Information obtained through cookies set by this application will be subject to this policy.',
'information_security' => 'Information Security',
'information_security_content' => '(a) This application accounts have security protection functions. Please keep your username and password information properly. This application will ensure that your information is not lost, abused, or altered through security measures such as encrypting user passwords. Despite the aforementioned security measures, please also note that there is no "perfect security measure" on information networks. (b) When using this application\'s network services for online transactions, you will inevitably need to disclose your personal information to the transaction counterparty or potential transaction counterparty, such as contact information or postal address. Please properly protect your personal information and only provide it to others when necessary. If you discover that your personal information has been leaked, especially if your application username and password are compromised, please contact this application\'s customer service immediately so that this application can take appropriate measures.',
'policy_changes' => 'Changes to This Privacy Policy',
'policy_changes_content' => '(a) If we decide to change our privacy policy, we will post these changes in this policy, on our company website, and in locations we deem appropriate so that you can understand how we collect and use your personal information, who can access this information, and under what circumstances we will disclose this information. (b) Our company reserves the right to modify this policy at any time, so please check it frequently. If we make significant changes to this policy, our company will notify you through website notifications.'
],
'jump' => [
'auto_redirect' => 'seconds remaining, redirecting',
'redirect_now' => 'Redirect Now'
],
'controller' => [
'success' => 'success',
'fail' => 'fail',
'price_error' => 'Price error',
'server_error' => 'Server error',
'no_registration_info' => 'No registration information found',
'registration_expired' => 'The registration you are searching for has expired or been cancelled',
'registration_success' => 'Registration successful',
'registration_failed' => 'Unsuccessful registration',
'registration_pending' => 'Registration in progress',
'visa_revoked' => 'Your visa has been revoked',
'missing_order_sn' => 'Missing order number, please fill in again',
'order_not_exist' => 'Order number does not exist, please fill in again',
'payment_success' => 'Payment successful',
'parameter_error' => 'Parameter error',
'payment_not_completed' => 'Payment not completed',
'order_not_exist_retry' => 'Order number does not exist, please pay again',
'payment_not_completed_retry' => 'Payment not completed, please try again',
'no_records_found' => 'No records found',
'china_passport_invalid' => 'Invalid Chinese passport number format',
'submit_success' => 'Submitted successfully',
'submit_failed' => 'Submission failed',
'upload_missing' => 'No file uploaded or upload failed',
'upload_success' => 'File uploaded successfully',
'upload_failed' => 'File upload failed',
],
'pay' => [
'thank_you_message' => 'Thank you for your application:',
'order_submitted' => 'Your order has been submitted. Order number: %order_sn%',
'processing_time_info' => 'Normally, your documents will be submitted to %country% immigration within 1-2 working days. In some cases, the approval result will be made within 72 hours. We will notify you by email to download the approval documents. If your application is rejected by %country% immigration, we will contact you immediately.',
'visa_application_button' => 'Visa Application √',
'contact' => 'If you have any questions, please feel free to submit feedback through "Contact Us" at any time.',
'dialog_title' => 'Notice',
'payment_confirmation' => 'Please confirm if WeChat Pay is completed',
'repay_button' => 'Pay Again',
'arrive_date_info' => 'Your travel date is %s. The system submission node has not been reached yet. The system will automatically submit your application on the preset submission date %s. After your submission is successful, we will notify you by email to download the approval document. Please check your email in time.',
'confirm_button' => 'Payment Completed'
],
'foot' => [
'company_address' => 'Company Address',
'user_agreement' => 'Terms of Service',
'privacy_policy' => 'Privacy Policy',
'disclaimer' => 'Disclaimer',
'refund_policy' => 'Refund Policy',
'pay_policy' => 'Payment Terms',
'cookie_policy' => 'Cookies Policy',
'user_agreement_info' => '<div style="padding: 20px;"><div class="disclaimer">
Please note: Using this website indicates that you have read, understood, and agreed to comply with all the following terms and conditions. If you do not agree to these terms, please do not use our services.
</div>
<div class="terms-container">
<div class="term-item">
<h5 class="term-title">
<span class="term-number">1</span>
Service Content
</h5>
<div class="term-content">
<p>Our company (ITS INTERNATIONAL TRAVEL SERVICE COMPANY) is a professional institution providing travel-related services, specializing in assisting clients with EVUS registration applications.</p>
<div class="highlight">
<p>Important Note: Our company only provides form filling guidance, information review, and technical assistance services related to EVUS registration, and is not affiliated with any government agencies. Our services aim to help clients complete the application process more accurately and avoid common errors.</p>
</div>
<p>Our services include: assisting with filling out EVUS application forms, reviewing provided information, submitting applications to the official system, and providing necessary technical support during the application process. Please note that we cannot guarantee that the application will be approved, as the final decision rests entirely with U.S. Customs and Border Protection (CBP).</p>
</div>
</div>
<div class="term-item">
<h5 class="term-title">
<span class="term-number">2</span>
Fee Explanation
</h5>
<div class="term-content">
<p>Using our EVUS registration service requires payment of corresponding service fees, which include official application fees that may be charged by the U.S. government (if applicable).</p>
<p>Our service fee structure is clear and transparent. Users must pay the service fee according to the prices published on this website. Service fees may be adjusted without notice, but applications already started will be executed according to the fee standards applicable at the time of application initiation.</p>
<div class="highlight">
<p>Fees include: application guidance services, information review services, technical support services, and subsequent application status inquiry assistance. All fees are clearly informed before the service begins, and users need to complete payment before continuing with the service.</p>
</div>
</div>
</div>
<div class="term-item">
<h5 class="term-title">
<span class="term-number">3</span>
User Responsibility
</h5>
<div class="term-content">
<p>When using this service, users are responsible for providing true, accurate, complete, and up-to-date personal information. This information includes but is not limited to: name, date of birth, passport information, visa information, and other data required for EVUS application.</p>
<p>Users should ensure that all provided materials are true and valid. If EVUS application fails, is delayed, or is rejected due to incorrect, incomplete, outdated, or misleading information provided by the user, our company shall not bear any responsibility.</p>
<div class="highlight">
<p>User declares and guarantees: All provided information is true and accurate; has legal rights to provide this information; this information does not infringe upon any third-party rights; and using this service does not violate any applicable laws and regulations.</p>
</div>
</div>
</div>
<div class="term-item">
<h5 class="term-title">
<span class="term-number">4</span>
Company Liability Limitations
</h5>
<div class="term-content">
<p>Our company provides EVUS application assistance services, not official application channels. The outcome of EVUS registration is entirely determined by U.S. Customs and Border Protection (CBP), and our company cannot influence or control the approval results.</p>
<p>Therefore, our company does not assume any guarantees or promises regarding EVUS application results. Regardless of whether the application is successful or not, paid service fees are non-refundable as the service has been completed upon submission of the application.</p>
<div class="highlight">
<p>Liability limitation: To the maximum extent permitted by law, our company and its employees and agents shall not be liable for any indirect, incidental, special, consequential damages, or loss of profits arising from the use of this service. Our maximum liability is limited to the amount of service fees paid by the user.</p>
</div>
</div>
</div>
<div class="term-item">
<h5 class="term-title">
<span class="term-number">5</span>
Dispute Resolution
</h5>
<div class="term-content">
<p>These service terms and any disputes arising from the use of this service shall be governed by the laws of California, USA, without regard to its conflict of law provisions.</p>
<p>Any disputes related to these service terms should first be resolved through friendly negotiation. If negotiation fails, both parties agree to submit the dispute to the competent court in Los Angeles County, California for resolution.</p>
<div class="highlight">
<p>User agrees: Regardless of location, using this service indicates acceptance of the exclusive jurisdiction of California courts and waives any objections to the jurisdiction and venue of such courts.</p>
</div>
</div>
</div>
</div></div>',
'privacy_policy_info' => ' <div style="padding: 20px;"><div class="section" id="intro">
<p>ITS INTERNATIONAL TRAVEL SERVICE COMPANY (hereinafter referred to as "the company", "we", "our") is firmly committed to regulatory compliance and the privacy and protection of personal data. Therefore, in this privacy policy, users (hereinafter referred to as "user", "you", "your") will find all relevant information to better understand how we process your personal data.</p>
<p>We may update this privacy policy based on new legislation or judicial requirements and/or business needs, etc. Any updates or modifications to this privacy policy will be considered applicable from the date of its publication on the website. Therefore, it is recommended that users regularly review this privacy policy.</p>
<p>Please remember that to access and use our website, you must be an adult and have the legal capacity to enter into contracts according to the national laws and regulations applicable in your country of birth or residence.</p>
<p>Without prejudice to your rights under applicable law, this privacy policy is not a contract or part of your contract with us. This privacy policy will always be available on this website so that data subjects can consult it at any time.</p>
</div>
<div class="section" id="data-definition">
<h4>2. Definition of Personal Data</h4>
<p>For the purposes of this privacy policy, "personal data" means all data that identifies a data subject or can be used to identify him/her, such as the data you provide when filling out the application form to obtain or update EVUS registration for travel to the United States. Additionally, we process your personal data when you contact our customer service team, as well as process other information provided during your browsing of this website or from your IP address.</p>
</div>
<div class="section" id="data-controller">
<h4>3. Data Controller</h4>
<p>Our company, as described in our terms and conditions, will be the controller of any personal data collected from you, including but not limited to, when you browse our website, fill out your application form to obtain or update EVUS registration, and/or when you contact our customer service team via email.</p>
</div>
<div class="section" id="scope">
<h4>4. Scope of Application</h4>
<p>This privacy policy applies to personal data we collect, use, and process from you as users of our website and/or customers.</p>
<p>Our website may contain features or links that may redirect you to third-party websites. In this case, please note that these third-party websites are not operated by us, so we are not responsible for the information and content they provide, nor for the correct provision of functions and/or services offered by these third parties as we cannot control this. These third-party websites are governed by their own cookie and privacy policies.</p>
</div>
<div class="section" id="data-collection">
<h4>5. Information Collection</h4>
<p>Our company may collect your name, passport information, contact details, and payment information, solely for EVUS registration services.</p>
<p>We collect the following categories of personal data:</p>
<p>1. Direct interactions, such as when you complete an application on our website to obtain or update EVUS registration, or when we request additional information from you as required by U.S. consulates or embassies, or when you fill out our contact form, or when you may contact our customer service team directly via email.</p>
<p>2. Automated technologies, we may automatically collect information such as your IP address, sections you consult on our website, or time spent on our website through the use of our own or third-party cookies. You can consult our Cookie Policy for more information about the use of cookies on our website, their purposes, and other information of interest.</p>
<p>3. Payment data, we will only request credit or debit card information related to our service fees on the payment page. The captured data will be securely sent to our certified payment service provider to authorize the corresponding transaction. Under no circumstances will we visualize or access the complete data of your credit or debit card. If the customer enters, provides, or sends his/her card information through channels not authorized by our company, the customer is responsible.</p>
<div class="highlight">
<p>In all cases, we only process personal data necessary to achieve the purposes mentioned above. In this sense, if you provide us with information beyond what is explicitly requested, you consent to the processing of said information for the informed purpose for which you sent it.</p>
</div>
<p>To be able to properly use our services now and at any time thereafter, you must provide us with accurate, true, up-to-date, and complete information about your current personal situation.</p>
<p>If you provide us with personal data of third parties, whether minors or adults, you confirm that you have informed them of the purpose of processing their personal data, and you guarantee that you have obtained their prior explicit consent to communicate their personal data to us. In this sense, if you provide us with personal data of minors, you guarantee that you are the parent and/or legal guardian, and therefore, you agree to the processing of your child/wards personal data.</p>
</div><div class="section" id="data-usage">
<h4>6. Information Usage</h4>
<p>Your information is only used for processing EVUS registration and customer service, and will not be sold or shared without authorization.</p>
<p>Please note that you only need to provide the personal data and information necessary to fill out the application form you selected to obtain and/or through our contact form. In this sense, if you provide us with information beyond what is explicitly requested, it means you consent to its processing for the purpose for which you sent the information.</p>
<p>We use your personal data for the following purposes:</p>
<ul>
<li>To provide you with our services, and if necessary, if the provided information/documents are incomplete and/or incorrect, we may also request additional information/documents from you.</li>
<li>Process information and/or refund inquiries you send to our customer service department.</li>
<li>Send you operational emails about our services, such as payment confirmation emails.</li>
<li>For administrative purposes related to processing and managing the services provided, such as accounting and billing tasks and verifying your payment information.</li>
<li>Comply with our legal obligations, legal requirements, laws and regulations, and/or respond to requests from judicial, police, or other authorities.</li>
<li>Protect, investigate, and prevent fraud, unauthorized or illegal activities on this website or through this website.</li>
<li>Improve our website, services, and customer support, and enhance your user experience on our website by analyzing your behavior while browsing this website.</li>
</ul>
</div>
<div class="section" id="legal-basis">
<h4>7. Legal Basis</h4>
<p>We only process your personal data when there is a legal basis. The legal basis will depend on the reason we collect and process your personal data. In almost all cases, the legal basis is:</p>
<ul>
<li>Your consent for us to process your personal data related to the purposes informed when collecting your data (i.e., responding to your information inquiries when you contact our customer service team).</li>
<li>Performance of a contract when purchasing our services or sending you operational emails to keep you updated when sending payment confirmation emails.</li>
<li>Compliance with our legal obligations and/or meeting official requirements from administrative, judicial, police, or corresponding authorities.</li>
<li>Our legitimate interests in: (i) providing an enhanced user experience and effectively operating this website when you access and use it, (ii) verifying customer identity and monitoring fraudulent activities to maintain the security and integrity of our website; (iii) defending and resolving our rights in case of claims against our professional services.</li>
</ul>
</div>
<div class="section" id="data-retention">
<h4>8. Data Retention</h4>
<p>Information will be stored on secure servers for a period not exceeding 12 months after application completion.</p>
<p>We will only retain your personal data for the time necessary to achieve its processing purposes. Beyond that, your personal data will be retained and appropriately blocked until the company complies with its legal obligations and has satisfied its business needs, objectives, and strategies. In this sense, your personal data will be retained, for example, to address and defend any claims related to the services provided; until your information and/or refund requests are answered; complete investigations of detected fraudulent or illegal activities; if you do not revoke your consent, we will retain cookie data.</p>
<p>Once the necessary retention period ends, your personal data will be securely deleted from our information systems.</p>
<p>Furthermore, if you consent to us processing your personal data for specific purposes, please note that you can withdraw this consent at any time. Upon receiving your withdrawal request, we will proceed to delete your personal data.</p>
</div>
<div class="section" id="data-sharing">
<h4>9. Data Sharing</h4>
<p>When necessary, we may share your information with payment service providers or the EVUS system.</p>
<p>We may communicate your personal data to the following third parties, including but not limited to:</p>
<ul>
<li>U.S. consulates or embassies to submit applications</li>
<li>Credit and debit card companies to process payments and (when necessary) conduct fraud checks</li>
<li>Public agencies and/or administrative or judicial bodies, and police, when required by applicable laws and regulations</li>
<li>Law firms to respond to any claims related to our services and/or protect our legitimate interests</li>
<li>Technical service providers that host our information systems and/or provide us with technology, tools, or support</li>
<li>Suppliers that assist us in properly providing services</li>
</ul>
<p>The headquarters of the aforementioned third parties may be located within the European Economic Area; therefore, your personal data will not be subject to international transfer. In this regard, we inform you that we will only make international transfers of personal data to recipients established in countries that provide an adequate level of data protection; otherwise, we will adopt appropriate safeguards required by current applicable laws and regulations regarding the protection of personal data to ensure that your personal data is properly protected and your personal rights and freedoms are guaranteed.</p>
</div>
<div class="section" id="data-security">
<h4>10. Data Security</h4>
<p>Your trust is very important to us. Therefore, your personal data will be stored confidentially and securely in our information systems. We have implemented appropriate technical and organizational measures to protect and safeguard your personal data against illegal or unauthorized access, loss or accidental destruction, damage, use, and illegal or unauthorized disclosure.</p>
<p>Similarly, we have taken reasonable precautions to ensure that all our employees and vendors or collaborators who have access to customers personal data are adequately trained in the processing of collected personal data and that they comply with the aforementioned data protection obligations.</p>
<p>Although we will do our best to guarantee the protection of personal data transmitted through our website and/or that you may provide to us through other means such as email, please remember that you should also take precautions to keep your personal data secure, for example, you must not enter or confirm banking details through false information and/or websites.</p>
</div>
<div class="section" id="user-rights">
<h4>11. User Rights</h4>
<p>You have the right to request to view, correct, or delete your personal information.</p>
<p>You can exercise your rights through our contact form. You can modify the "privacy settings" of your web browser at any time to set tracking cookies. Additionally, you can install programs or plugins in your browser, known as "Do Not Track" tools, which will allow you to choose which cookies you want to allow.</p>
<p>Generally, we respond to data protection rights requests within one (1) month. Sometimes, this period may be extended by another two (2) months if necessary, considering whether your request is particularly complex or if you have made multiple requests. In this case, we will notify you of this event and explain the reason for the delay.</p>
<p>Finally, we inform you that, where you deem appropriate, you have the right to file a complaint with the corresponding regulatory authority, especially if you believe that your data protection rights have not been adequately addressed.</p>
</div>
<div class="section" id="cookies">
<h4>12. Cookie Policy</h4>
<p>Unless you refuse to accept cookies, this application will set or access cookies on your computer to allow you to log in or use this applications platform services or features that rely on cookies. This application uses cookies to provide you with more comprehensive personalized services, including promotional services.</p>
<p>You have the right to choose to accept or refuse cookies. You can refuse to accept cookies by modifying your browser settings. However, if you choose to refuse cookies, you may not be able to log in or use this applications web services or features that rely on cookies.</p>
<p>Information obtained through cookies set by this application will be subject to this policy.</p>
</div>
<div class="section" id="updates">
<h4>13. Policy Updates</h4>
<p>If we decide to change the privacy policy, we will publish these changes in this policy, on our companys website, and in locations we deem appropriate so that you are aware of how we collect and use your personal information, who can access this information, and under what circumstances we disclose it.</p>
<p>Our company reserves the right to modify this policy at any time, so please check it frequently. If significant changes are made to this policy, our company will inform you through website notifications.</p>
</div>
<div class="section" id="contact">
<h4>14. Contact Us</h4>
<div class="contact-info">
<p>If you have any questions or concerns about this privacy policy, you can contact our customer service team.</p>
<p>If you discover that your personal information has been leaked, especially if your application username and password have been compromised, please immediately contact our application customer service so that we can take appropriate measures.</p>
</div>
</div></div>',
'disclaimer_info' => '<div style="padding: 20px;">
<div class="disclaimer-item">
<h4>1. Non-Official Nature Statement</h4>
<p>This website is not an official U.S. government website, nor is it affiliated with U.S. Customs and Border Protection (CBP). We are an independent service provider specializing in assisting applicants with the EVUS registration process.</p>
</div>
<div class="disclaimer-item">
<h4>2. Service Scope Explanation</h4>
<p>ITS INTERNATIONAL TRAVEL SERVICE COMPANY only provides EVUS form filling assistance and related technical support services. We do not make any guarantees or promises regarding the final approval results, as the approval authority rests entirely with relevant U.S. government departments.</p>
</div>
<div class="disclaimer-item">
<h4>3. Approval Authority Statement</h4>
<p>Whether an EVUS application is approved is entirely determined by relevant U.S. government departments. We cannot influence or expedite the approval process. Our services are limited to assisting you in correctly completing and submitting application forms.</p>
</div>
<div class="note">
<p>Using this service indicates that you have read, understood, and agreed to the above disclaimer.</p>
</div>
</div>
</div>',
'refund_policy_info' => '<article class="card">
<h1>ITS INTERNATIONAL TRAVEL SERVICE COMPANY Refund Policy</h1>
<p class="intro">Providing professional assistance services that satisfy our customers has always been our core commitment. If you are dissatisfied with our services, you may submit a refund request in accordance with the policy below.</p>
<section>
<h2>I. Application Process and Time Limits</h2>
<p>To ensure that your request is handled promptly and properly, please strictly follow the process below:</p>
<ul>
<li><strong>Exclusive application channel:</strong> All refund requests must be submitted through our official contact form, with the reason for the request clearly explained. Please avoid opening disputes through external payment institutions, as doing so may suspend our review process and cause significant delays in refund processing.</li>
<li><strong>Standard submission period:</strong> Customers must submit a refund request within 30 days after receiving the payment confirmation email. Late requests will not be accepted, except in extremely special force majeure circumstances, which we may review at our sole discretion.</li>
<li><strong>Bulk order processing:</strong> If your order contains multiple electronic travel document applications, please submit a separate refund form for each individual document application.</li>
</ul>
</section>
<section>
<h2>II. Refund Eligibility and Service Fee Deductions</h2>
<p>Once an order has been successfully submitted, our system and support team immediately begin the corresponding processing work. Therefore, all refund requests will be handled in strict accordance with the following deduction and refund standards:</p>
<ul>
<li><strong>Basic service fee deduction (important):</strong> Once an order has been successfully submitted, if your refund request is ultimately approved, we will deduct 10% of the total order amount as a basic processing and manual service fee, and only the remaining balance will be refunded.</li>
<li>
<strong>Refundable cases (remaining balance refunded after 10% deduction):</strong>
<ul class="sub-list">
<li>As of the date you submit the refund request, your electronic travel document application has not yet been formally submitted to the relevant government authority.</li>
<li>Your electronic travel document application is ultimately refused by the competent government authority.</li>
</ul>
</li>
<li>
<strong>Non-refundable cases:</strong>
<ul class="sub-list">
<li><strong>Full service fee:</strong> If your document application has already formally entered the processing stage, our professional assistance service fee is non-refundable.</li>
<li><strong>Government fees:</strong> Any assessment or government fee incurred during the approval process is non-refundable once charged.</li>
<li><strong>Standard add-on services:</strong> Fees for add-on services, such as expedited processing, are non-refundable once such services have been initiated.</li>
</ul>
</li>
</ul>
</section>
<section>
<h2>III. Review and Refund Timeline</h2>
<ul>
<li><strong>Review period:</strong> We will complete the review within 24 hours after receiving your form. Regardless of the outcome, we will send you written notice by email.</li>
<li><strong>Refund processing:</strong> Once a refund request is approved, we will return the funds within 72 hours to the customer\'s original payment method.</li>
<li><strong>Posting time:</strong> The actual arrival of funds usually takes 10 to 15 business days. This timeline depends on the settlement cycle of the card-issuing bank or third-party payment institution and is not under our control. We appreciate your understanding.</li>
</ul>
</section>
<section class="contact">
<h2>IV. Contact Us</h2>
<p>If you have any questions about this refund policy or need further assistance, please feel free to contact our customer service team at any time:</p>
<ul>
<li><strong>Email:</strong> <a href="mailto:info@itsvisa.com">info@itsvisa.com</a></li>
</ul>
</section>
<section>
<h2 style="color: red">V. Legal Rights Protection and Jurisdiction Statement</h2>
<p style="color: red">Our company always adheres to the principle of honest operation and is committed to providing high-quality assistance services to customers. The above statements and these terms are protected by the laws of the United States. In the event of any malicious complaint, abuse of the refund process, infringement of our lawful interests, or conduct that affects the normal operation of our company, we reserve the right to pursue all available remedies and to initiate cross-border legal action to safeguard our rights in accordance with law.</p>
</section>
</article>"',
'cookie_policy_info' => ' <div style="padding: 20px;">
<div class="cookie-notice">
<h3>Cookie Usage Instructions</h3>
<p>This website is operated by ITS INTERNATIONAL TRAVEL SERVICE COMPANY and uses Cookie technology to enhance user experience.</p>
<h4>Our main purposes for using Cookies:</h4>
<ul>
<li>Remember your login status and preference settings</li>
<li>Analyze website traffic and user behavior patterns</li>
<li>Optimize website performance and improve service quality</li>
<li>Provide personalized content recommendations</li>
</ul>
<h4>Cookie Management:</h4>
<p>You can restrict or disable Cookies through your browser settings. Please note that if you disable Cookies, some website features may not function properly, including but not limited to:</p>
<ul>
<li>Unable to maintain login status, requiring re-login each visit</li>
<li>Personalized settings cannot be saved</li>
<li>Some services may be completely unavailable</li>
</ul>
<p>Continuing to use this website indicates your agreement to our use of Cookie technology as described in this notice.</p>
</div>
</div>'
]
];

732
app/lang/zh-cn.php Normal file
View File

@@ -0,0 +1,732 @@
<?php
return [
'contact' => [
'info' => '如果您对您的申请有任何疑问请填写以下表格。我们的客户服务团队将在24小时内回复您。',
'full_name' => '全名',
'email' => '电子邮箱',
'repeat_email' => '确认电子邮箱',
'passport_number' => '护照号码',
'is_apply' => '您是否已经通过我们提交申请?',
'contact_reason' => '联系原因',
'order_sn' => '订单号',
'comment' => '留言',
'yzm' => '验证码',
'change' => '换一张',
'menu_contact' => '联系我们',
'yzm_error' => '验证码错误',
'email_error' => '邮箱错误',
'yes' => '是',
'no' => '否',
'submit_now' => '立即提交',
'optional' => '(选填)',
'default_reqtext' => '必填项不能为空',
'success' => '操作成功',
'fail' => '操作失败',
],
'wepay' => [
'payment_title' => '订单支付',
'order_success_notice' => '订单提交成功,请尽快完成付款',
'order_number' => '订单号',
'amount_due' => '应付金额',
'currency_unit' => '美元',
'wechat_pay' => '微信支付',
'alipay' => '支付宝',
'scan_to_pay' => '请使用手机扫码完成支付',
'scan_notice' => '支付完成后页面会自动跳转,请不要关闭或刷新页面。',
'processing' => '正在等待支付结果...',
'missing_order_sn' => '缺少订单号,请重新填写申请。',
'system_error' => '异常错误,请重新提交。',
'already_paid' => '您已支付成功,请勿重复支付。',
'payment_success' => '支付成功',
'payment_not_completed' => '您还未支付,请重新支付。',
'payment_failed_retry' => '支付失败,请稍后重试。',
'order_missing_retry' => '订单号不存在,请重新支付。',
'pay_body' => '电子签登记服务费',
'success_title' => '感谢您的申请',
'success_order_notice' => '您的订单已提交,订单号为:',
'success_desc' => '我们会尽快处理您的申请,请留意邮件通知。',
'success_tip_a' => '如遇临近出行或需要加急处理,请第一时间联系客服确认可用通道。',
'success_tip_b' => '通常情况下,资料会在规定时效内提交至相关机构,个别申请可能需要额外审核时间。',
'apply_again' => '继续申请'
],
'head' => [
'title' => '泰国入境卡中文登记系统_在线申请与登记',
'keywords' => '泰国入境卡,泰国入境卡中文网,泰国入境卡登记系统,泰国入境卡在线申请,泰国出入境登记',
'description' => '泰国入境卡中文登记系统提供便捷的入境卡在线登记服务,简化泰国出入境流程,操作简单,高效完成入境卡申请。',
'og_title' => '泰国入境卡中文登记系统_在线申请与登记',
'og_description' => '提供泰国入境卡中文登记服务,让您高效完成入境卡在线申请与登记。'
],
'index' => [
'notice' => '请注意:',
'notice_text'=>'本网站为独立运营的商务服务平台凭借自主研发的智能系统带来三大核心服务智能填表系统可高效简化表单填写流程精准OCR识别能快速提取图文信息智能AI翻译则支持多语种精准转换。专注为用户提供专业有偿服务不隶属于任何政府部门',
'home' => '首页',
'online_application' => '在线申请',
'entry_card_query' => '入境卡查询',
'usage_help' => '新闻资讯',
'news_author' => '作者',
'news_time' => '时间',
'visa_service' => '签证服务',
'welcome_message' => '欢迎使用%country%入境卡中文网登记系统',
'process_title' => '入境登记操作指引',
'step1_title' => '拍照识别护照信息',
'step1_desc' => '仅需护照首页,自动识别姓名/护照号/生日等字段(可手动修正)',
'step2_title' => '填写行程并提交申请',
'step2_desc' => '核对行程目的、停留时间、在新加坡地址/联系人等信息一致性。',
'step3_title' => '查询下载登记文件',
'step3_desc' => '支付完成后可通过订单号查询并下载文件/确认页,便于值机或入境备查',
'tip'=>'小提示',
'tip_1'=>'英文翻译要自然(职业/单位性质/行程目的)',
'tip_2'=>'行程与在境内地址前后一致',
'tip_3'=>'如临近出发,可选择加急通道(以系统为准)',
'tip_4'=>'建议保存 PDF/截图备用',
'entry_card_application' => '入境卡申请',
'application_description1' => '所有到%country%旅游的旅客必须持有%country%入境卡才能进入该国。欲获取%country%电子入境卡且符合资格的人士,均需在线填写一份简单明了的申请表。',
'application_notice' => '请注意:所有到达%country%的旅客必须提交一份健康申报表,以便通过边境的移民检查。健康声明可作为电子入境卡网上申请服务的一部分,填写并提交。',
'apply_now' => '入境卡申请√',
'electronic_arrival_card' => '电子入境卡',
'environmental_initiative' => '为响应环保倡议,%country%政府自即日起全面启用电子入境卡系统,取代传统纸质出境记录卡。',
'digital_process_benefits' => '该系统通过护照信息自动关联,实现入境流程数字化,为访客提供便利,全流程线上申请,节省线下办理时间,实时更新入境状态,提升通关效率。',
'submission_requirement' => '所有旅客均须在抵达%country%前三 (3) 天内(包括抵达当天)提交%country%入境卡,可以在线填写,也可到达机场再填写。',
'card_validity' => '%country%电子入境卡自你在申请表上指明的抵达日开始有效,且仅适用于单次入境。',
'why_choose_us_title' => '服务优势',
'why_choose_us_subtitle' => '让登记更清晰、更省心。',
'convenient' => '方便快捷',
'convenient_desc' => '中文引导+关键字段校验,减少反复修改。',
'secure' => '安全可靠',
'secure_desc' => '加密传输,隐私信息更安心。',
'approval' => '减少退回',
'approval_desc' => '经验要点提醒,降低常见错误率。',
'support' => '在线支持',
'support_desc' => '遇到问题可联系客服协助处理。',
'clients' => '服务客户',
'clients_desc' => '已服务客户覆盖了多个行业和领域,展现了我们强大的业务能力和客户信赖。',
'service' => '服务支持',
'service_desc' => '我们提供全年无休的 7×24 小时服务支持,随时响应您的需求。',
'experience' => '工作年限',
'experience_desc' => '我们拥有 10 年的深厚行业经验,积累了丰富的专业知识和实践能力。',
'approval_rate' => '签证批准率',
'approval_rate_desc' => '我们的签证申请批准率始终保持在业内领先水平,得益于我们丰富的经验、专业的团队',
'setmeal'=>'办理套餐与时效',
'setmeal_st'=>'标准办理',
'setmeal_ex'=>'加急办理',
'setmeal_team'=>'企业/团队',
'suitable_for_advance_preparation'=>'适合提前准备',
'processing_time_limit'=>'处理时效以系统展示为准',
'chinese_guided_filling'=>'中文引导填写',
'key_field_consistency_check'=>'关键字段一致性校验',
'basic_error_reminder'=>'基础错误提醒',
'suitable_for_departure_soon'=>'适合临近出发',
'urgent_availability'=>'加急可用性以系统为准',
'priority_processing_queue'=>'优先处理队列',
'stricter_reminder_for_key_fields'=>'重点字段提醒更严格',
'faster_feedback_and_supplementary_suggestions'=>'更快反馈与补充建议',
'group_travel_batch_reg'=>' 多人同行 / 批量登记 ',
'group_meta_info'=>' 支持表格导入与专人对接 ',
'batch_data_collect'=>' 批量资料收集与整理 ',
'unified_verify_submit'=>' 统一校验与提交 ',
'progress_track_reconcile'=>' 进度跟踪与对账 ',
'select_standard_plan'=>' 选择标准方案 ',
'select_urgent_plan'=>' 选择加急方案 ',
'contact_team_plan'=>' 联系团队方案 ',
'common_questions'=>' 常见问题 ',
'back_to_top'=>' 返回顶部 ',
// FAQ相关
'faq_is_official_website' => '这是不是官网?',
'faq_is_official_website_answer' => '不是。本网站为独立运营的商务服务平台,提供有偿代理服务与填写协助,不隶属于任何政府部门。',
'faq_guarantee_approval' => '是否保证一定通过?',
'faq_guarantee_approval_answer' => '无法保证。我们提供信息整理、填写协助与错误规避建议,最终结果由相关机构审核决定。',
'faq_query_download_after_payment' => '支付后如何查询下载?',
'faq_query_download_after_payment_answer' => '进入“入境卡查询”页面输入订单号查询,下载确认页/文件;如查询不到,可联系客服协助核对。',
],
'visa' => [
'yellow_fever' => '您是否有黄热病疫苗接种证明?',
'yellow_fever_time' => '接种疫苗时间',
'step1_title' => '在线提交申请',
'step2_title' => '检查和确认付款',
'step3_title' => '收到获批确认',
'verify'=>'请仔细核实信息, 核实后,不能做任何修改',
'personal_details_title' => '个人详细信息',
'passport_number' => '护照号码',
'passport_number_placeholder' => '请输入或拍照识别',
'upload_passport_title' => '拍照/上传有效护照照片',
'nationality' => '国籍',
'select_nationality_placeholder' => '直接选择或搜索选择国籍',
'last_name_en' => '姓氏(英文)',
'confirm_name' => '姓名',
'last_name_placeholder' => '请输入姓氏',
'first_name_en' => '名字(英文)',
'first_name_placeholder' => '请输入名字',
'middle_name' => '中间名',
'optional' => '非必填',
'middle_name_placeholder' => '请输入中间名,无中间名请不要填写',
'birth_date' => '出生日期',
'birth_date_placeholder' => '请选择出生日期',
'occupation' => '职业',
'occupation_placeholder' => '请输入职业',
'email_address' => '电子邮件地址',
'email_placeholder' => '请输入电子邮件地址',
'phone_number' => '手机号码',
'phone_number_placeholder' => '请输入手机号码',
'residence_country' => '居住国家',
'select_residence_country_placeholder' => '直接选择或搜索选择居住国家',
'residence_city' => '居住城市',
'residence_city_placeholder' => '请输入居住城市',
'visa_number' => '签证号码',
'visa_number_placeholder' => '请输入签证号码',
'gender' => '性别',
'male' => '男性',
'female' => '女性',
'unknown' => '未知',
'next_step' => '下一步',
'travel_information_title' => '出行信息',
'arrival_date' => '入境日期',
'arrival_date_placeholder' => '请选择入境日期',
'departure_date' => '离境日期',
'departure_date_placeholder' => '请选择离境日期',
'departure_country' => '登机国家/地区',
'select_departure_country_placeholder' => '直接选择或搜索选择登机国家/地区',
'travel_purpose' => '旅行目的',
'select_travel_purpose_placeholder' => '请选择旅行目的',
'holiday' => '假期/旅游',
'meeting' => '会议',
'sports' => '体育赛事',
'business' => '商务',
'incentive' => '激励',
'medical' => '医疗健康',
'education' => '教育',
'convention' => '大会/论坛',
'employment' => '工作/就业',
'exhibition' => '展览',
'other_purpose' => '其他(请注明)',
'other_purpose_specify' => '其他旅游目的',
'other_purpose_placeholder' => '请输入其他旅游目的',
'entry_transport' => '入境交通方式',
'air' => '空',
'land' => '陆',
'sea' => '海',
'transport_number' => '入境航班号/船舶编号/运输编号',
'transport_number_placeholder' => '请输入航班号/船舶编号/运输编号',
'transport_mode' => '入境出行方式',
'out_transport_mode' => '出境出行方式',
'select_transport_mode' => '请选择出行方式',
'commercial_flight' => '商业航班',
'private_flight' => '私人/货运航班',
'other_transport' => '其他(请注明)',
'other_transport_specify' => '其他出行方式',
'other_transport_placeholder' => '请输入其他出行方式',
'car' => '汽车',
'train' => '火车',
'cruise' => '邮轮/游轮',
'commercial_vessel' => '商用船舶',
'departure_transport' => '出境交通方式',
'departure_transport_number' => '出境航班号/船舶编号/运输编号',
'departure_transport_placeholder' => '请输入航班号/船舶编号/运输编号',
'is_transit_passenger' => '是否是过境旅客',
'yes' => '是',
'no' => '否',
'accommodation_type' => '住宿类型',
'select_accommodation_type' => '请选择住宿类型',
'hotel' => '酒店',
'youth_hostel' => '青年旅舍',
'guest_house' => '宾馆/招待所',
'friends_place' => '朋友家',
'apartment' => '公寓',
'other_accommodation' => '其他(请注明)',
'other_accommodation_specify' => '其他住宿类型',
'other_accommodation_placeholder' => '请输入其他住宿类型',
'province' => '省份',
'select_province' => '请选择省份',
'city' => '城市',
'select_city' => '请选择城市',
'district' => '街道',
'select_district' => '请选择街道',
'address' => '地址',
'address_placeholder' => '请输入地址',
'visited_countries' => '请选择您抵达前两周内入住的国家/地区',
'applicant_declaration_title' => '申请人声明',
'declaration_statement' => '本人声明在此申请中提供真实、完整和正确的信息。',
'terms_acceptance' => '我已阅读并理解条款与条件及隐私政策。',
'select_package_title' => '选择套餐',
'currency_unit' => '元/人民币',
'submit_application_button' => '立即提交',
'select_payment_method' => '选择支付方式',
'wechat_pay' => '微信支付',
'alipay' => '支付宝',
'card' => '信用卡'
],
'sqpay' => [
'card'=>'信用卡',
'payment_title' => '在线支付',
'total_amount_label' => '您要支付的总金额',
'card_pay_title' => '银行卡支付',
'pay_now' => '立即支付',
'notice_title' => '通知信息',
'notice_text' => '支付将需要几分钟。在此同时,请不要点击浏览器的返回键、前进键或刷新键。',
'pay_success' => '支付成功',
'pay_failed' => '支付失败',
'success_notice' => '您的付款已经成功提交,系统将尽快处理您的申请,请留意邮件通知。'
],
'js' => [
'verify'=>'请核实您的信息',
'confirm'=>'确认',
'edit'=>'编辑',
'data_load_failed' => '数据加载失败:',
'request_error' => '请求出错,请稍后重试',
'select_city_placeholder' => '请选择城市',
'select_district_placeholder' => '请选择街道',
'select_package' => '请选择一个套餐',
'select_option_required' => '必须选择一个选项',
'alpha_numeric_only' => '只能输入字母和数字',
'select_country' => '请选择国家',
'alert_message' => '泰国移民局规定外国旅客必须在抵达泰国前3天内填写泰国数字入境卡表格。<br/>申请时间大于3天时订单将进入预约模式待申请时间触达规定范围系统将自动申请好入境卡',
'notice' => '注意',
'departure_date_error' => '离境日期不能早于入境日期',
'check_declaration' => '请勾选声明',
'uppercase_only' => '请填写大写字母',
'chinese_only' => '请填写中文',
'max_100_chars' => '请控制在100字以内',
'select_one_option' => '必须选择一个选项',
'english_only' => '只能输入英文字母',
'field_required' => '不能为空',
'upload_image_only' => '文件必须为图片',
'browser_not_supported' => '你的浏览器不支持上传',
'visa_unrecognized' => '无法识别,请手动填写签证号',
'manual_entry_required' => '无法识别,请手动填写',
'upload_retry' => '请重新上传',
'terms_privacy_title' => '条款与条件及隐私政策',
'terms_conditions' => '条款与条件',
'terms_intro' => '通过浏览、访问和使用本网站,您理解并同意此处设置的条款和条件,称为"条款及条件"和"隐私政策"。 通过本网站提交电子签证%country%申请的电子签证申请人将被称为"申请人"、"用户"、"您"。 "我们"、"我们"、"我们的"、"本网站"等术语直接指 本网站 重要的是,您要知道每个人的合法利益都受到保护,我们与您的关系是建立在信任基础上的。 请注意,您必须接受这些服务条款,才能使用我们的网站和我们提供的服务。',
'our_services' => '关于我们的服务我们的服务是作为在线申请服务提供商,用于促进电子签证流程,以便外国国民访问%country%。 我们的代理会协助您从%country%政府获得您的旅行授权,然后我们会向您提供该授权。 我们的服务包括正确审查您的所有信息、协助填写申请以及检查整个文件的准确性、完整性、拼写和语法审查。 此外,为了处理请求,我们可能会通过电子邮件或电话与您联系以获取更多信息。',
'application_process' => '填写我们网站上提供的申请表后,您的旅行授权文件请求将在专家审查后提交。 您的电子签证申请需要获得%country%政府的批准。但是,如果任何详细信息输入错误或不完整,您的申请可能会延迟或被拒。由您或指定的第三方代理人所提供的所有信息,必须是真实和正确的。如果您在填写信息时由您或您的代理人有意提交任何虚假、虚构或伪造的声明或陈述,造成%country%电子签证申请被拒,责任由您本人承担。 信息填写完成后,我们根据你所选择的套餐时效反馈结果,提交后信息将不能再更改。',
'read_confirmation' => '请表明您已阅读并了解上述所提供的信息',
'privacy_policy' => '隐私政策',
'privacy_intro' => '本应用尊重并保护所有使用服务用户的个人隐私权。为了给您提供更准确、更有个性化的服务,本应用会按照本隐私权政策的规定使用和披露您的个人信息。但本应用将以高度的勤勉、审慎义务对待这些信息。除本隐私权政策另有规定外,在未征得您事先许可的情况下,本应用不会将这些信息对外披露或向第三方提供。本应用会不时更新本隐私权政策。 您在同意本应用服务使用协议之时,即视为您已经同意本隐私权政策全部内容。本隐私权政策属于本应用服务使用协议不可分割的一部分。',
'scope' => '适用范围',
'scope_content' => '(a) 在您注册本应用帐号时,您根据本应用要求提供的个人注册信息; (b) 在您使用本应用网络服务或访问本应用平台网页时本应用自动接收并记录的您的浏览器和计算机上的信息包括但不限于您的IP地址、浏览器的类型、使用的语言、访问日期和时间、软硬件特征信息及您需求的网页记录等数据 (c) 本应用通过合法途径从商业伙伴处取得的用户个人数据。 您了解并同意,以下信息不适用本隐私权政策: (a) 您在使用本应用平台提供的搜索服务时输入的关键字信息; (b) 本应用收集到的您在本应用发布的有关信息数据,包括但不限于参与活动、成交信息及评价详情; (c) 违反法律规定或违反本应用规则行为及本应用已对您采取的措施。',
'information_use' => '信息使用',
'information_use_content' => '(a)本应用不会向任何无关第三方提供、出售、出租、分享或交易您的个人信息,除非事先得到您的许可,或该第三方和本应用(含本应用关联公司)单独或共同为您提供服务,且在该服务结束后,其将被禁止访问包括其以前能够访问的所有这些资料。 (b) 本应用亦不允许任何第三方以任何手段收集、编辑、出售或者无偿传播您的个人信息。任何本应用平台用户如从事上述活动,一经发现,本应用有权立即终止与该用户的服务协议。 (c) 为服务用户的目的,本应用可能通过使用您的个人信息,向您提供您感兴趣的信息,包括但不限于向您发出产品和服务信息,或者与本应用合作伙伴共享信息以便他们向您发送有关其产品和服务的信息(后者需要您的事先同意)',
'information_disclosure' => '信息披露',
'information_disclosure_content' => '在如下情况下,本应用将依据您的个人意愿或法律的规定全部或部分的披露您的个人信息: (a) 经您事先同意,向第三方披露; (b)为提供您所要求的产品和服务,而必须和第三方分享您的个人信息; (c) 根据法律的有关规定,或者行政或司法机构的要求,向第三方或者行政、司法机构披露; (d) 如您出现违反中国有关法律、法规或者本应用服务协议或相关规则的情况,需要向第三方披露; (e) 如您是适格的知识产权投诉人并已提起投诉,应被投诉人要求,向被投诉人披露,以便双方处理可能的权利纠纷; (f) 在本应用平台上创建的某一交易中,如交易任何一方履行或部分履行了交易义务并提出信息披露请求的,本应用有权决定向该用户提供其交易对方的联络方式等必要信息,以促成交易的完成或纠纷的解决。 (g) 其它本应用根据法律、法规或者网站政策认为合适的披露。',
'information_storage' => '信息存储和交换',
'information_storage_content' => '本应用收集的有关您的信息和资料将保存在本应用及(或)其关联公司的服务器上,这些信息和资料可能传送至您所在国家、地区或本应用收集信息和资料所在地的境外并在境外被访问、存储和展示。',
'cookie_use' => 'Cookie的使用',
'cookie_use_content' => '(a) 在您未拒绝接受cookies的情况下本应用会在您的计算机上设定或取用cookies 以便您能登录或使用依赖于cookies的本应用平台服务或功能。本应用使用cookies可为您提供更加周到的个性化服务包括推广服务。 (b) 您有权选择接受或拒绝接受cookies。您可以通过修改浏览器设置的方式拒绝接受cookies。但如果您选择拒绝接受cookies则您可能无法登录或使用依赖于cookies的本应用网络服务或功能。 (c) 通过本应用所设cookies所取得的有关信息将适用本政策。',
'information_security' => '信息安全',
'information_security_content' => '(a) 本应用帐号均有安全保护功能,请妥善保管您的用户名及密码信息。本应用将通过对用户密码进行加密等安全措施确保您的信息不丢失,不被滥用和变造。尽管有前述安全措施,但同时也请您注意在信息网络上不存在"完善的安全措施"。 (b) 在使用本应用网络服务进行网上交易时,您不可避免的要向交易对方或潜在的交易对方披露自己的个人信息,如联络方式或者邮政地址。请您妥善保护自己的个人信息,仅在必要的情形下向他人提供。如您发现自己的个人信息泄密,尤其是本应用用户名及密码发生泄露,请您立即联络本应用客服,以便本应用采取相应措施。',
'policy_changes' => '本隐私政策的更改',
'policy_changes_content' => '(a)如果决定更改隐私政策,我们会在本政策中、本公司网站中以及我们认为适当的位置发布这些更改,以便您了解我们如何收集、使用您的个人信息,哪些人可以访问这些信息,以及在什么情况下我们会透露这些信息。 (b)本公司保留随时修改本政策的权利,因此请经常查看。如对本政策作出重大更改,本公司会通过网站通知的形式告知。'
],
'lookup' => [
'status_query_title' => '查询状态',
'passport_number' => '护照号码',
'passport_number_placeholder' => '请输入护照号码',
'upload_passport_title' => '拍照/上传有效护照照片',
'birth_date' => '出生日期',
'birth_date_placeholder' => '请选择出生日期',
'last_name' => '姓氏',
'last_name_placeholder' => '请输入姓氏',
'last_name_tooltip' => '请参考护照上的信息用英文字母填写您的姓氏',
'first_name' => '名字',
'first_name_placeholder' => '请输入名字',
'first_name_tooltip' => '请参考护照上的信息用英文字母填写您的名字',
'query_status_button' => '查询状态',
'registered_status' => '已登记',
'approval_message' => '您的入境卡已被批准,您的申请已经完成。',
'field' => '字段',
'value' => '值',
'document' => '文件',
'status' => '状态',
'in_progress' => '办理中',
'click_download' => '点击下载',
'english_only' => '只能输入英文'
],
'jump' => [
'auto_redirect' => '秒后自动跳转',
'redirect_now' => '立即跳转'
],
'controller' => [
'success' => '操作成功',
'fail' => '操作失败',
'price_error' => '价格错误',
'server_error' => '服务器错误',
'no_registration_info' => '未检索到登记信息',
'registration_expired' => '您正在搜索的登记申请已过期或取消',
'registration_success' => '登记成功',
'registration_failed' => '登记不成功',
'registration_pending' => '登记中',
'visa_revoked' => '已撤销您的签证',
'missing_order_sn' => '缺少订单号,请重新填写',
'order_not_exist' => '订单号不存在,请重新填写',
'payment_success' => '订单已支付成功',
'parameter_error' => '参数错误',
'payment_not_completed' => '订单未支付',
'order_not_exist_retry' => '订单号不存在,请重新支付',
'payment_not_completed_retry' => '您还未支付,请重新支付',
'no_records_found' => '无法查到信息',
'china_passport_invalid' => '中国护照号码格式不正确',
'submit_success' => '提交成功',
'submit_failed' => '提交失败',
'upload_missing' => '未上传文件或上传失败',
'upload_success' => '文件上传成功',
'upload_failed' => '文件上传失败',
],
'pay' => [
'thank_you_message' => '感谢您的申请:',
'order_submitted' => '您的订单已提交,订单号为:%order_sn%',
'processing_time_info' => '通常情况下您的资料将在1-2个工作日内提交至%country%移民局个别情况下审批结果将在72小时之内做出。届时我们会通过邮件方式通知您下载批准文件。如果你的申请被%country%移民局拒绝,我们会在第一时间内联系您。',
'visa_application_button' => '签证申请√',
'dialog_title' => '提示',
'contact' => '如果有任何问题,请随时通过 “联系我们” 提交反馈。',
'payment_confirmation' => '请确认微信支付是否已完成',
'repay_button' => '重新支付',
'arrive_date_info' => '您的出行日期为 %s。现暂未到达系统提交节点系统将在预设的提交日期 %s 自动为您提交申请。提交成功后,我们会通过邮件方式通知您下载批准文件,请注意查收。',
'confirm_button' => '已完成支付'
],
'foot' => [
'company_address' => '公司地址',
'user_agreement' => '用户服务协议',
'privacy_policy' => '隐私政策',
'disclaimer' => '免责声明',
'refund_policy' => '退款政策',
'pay_policy' => '支付条款',
'cookie_policy' => 'Cookies使用声明',
'user_agreement_info' => '<div style="padding: 20px;"><div class="disclaimer">
请注意:使用本网站即表示您已阅读、理解并同意遵守以下所有条款和条件。如果您不同意这些条款,请勿使用我们的服务。
</div>
<div class="terms-container">
<div class="term-item">
<h5 class="term-title">
<span class="term-number">1</span>
服务内容
</h5>
<div class="term-content">
<p>本公司ITS INTERNATIONAL TRAVEL SERVICE COMPANY是一家专业提供旅行相关服务的机构专门协助客户完成EVUS登记申请。</p>
<div class="highlight">
<p>重要说明本公司仅提供EVUS登记相关的表格填写指导、信息审核和技术协助服务并不隶属于任何政府机构。我们的服务旨在帮助客户更准确地完成申请流程避免常见错误。</p>
</div>
<p>我们的服务包括协助填写EVUS申请表格、审核所提供的信息、提交申请至官方系统以及在申请过程中提供必要的技术支持。请注意我们无法保证申请一定获得批准因为最终决定权完全在于美国海关与边境保护局CBP。</p>
</div>
</div>
<div class="term-item">
<h5 class="term-title">
<span class="term-number">2</span>
费用说明
</h5>
<div class="term-content">
<p>使用本公司的EVUS登记服务需要支付相应的服务费用该费用包括美国政府可能收取的官方申请费用如有。</p>
<p>我们的服务费用结构清晰透明,用户须按照本网站公布的价格支付服务费。服务费可能在未经通知的情况下进行调整,但已经开始的申请将按照申请开始时适用的费用标准执行。</p>
<div class="highlight">
<p>费用包括:申请指导服务、信息审核服务、技术支持服务以及后续的申请状态查询协助。所有费用均在服务开始前明确告知,用户需要在继续服务前完成支付。</p>
</div>
</div>
</div>
<div class="term-item">
<h5 class="term-title">
<span class="term-number">3</span>
用户责任
</h5>
<div class="term-content">
<p>用户在使用本服务时有责任提供真实、准确、完整且最新的个人信息。这些信息包括但不限于姓名、出生日期、护照信息、签证信息以及其他EVUS申请所需的资料。</p>
<p>用户应确保所提供的所有资料均真实有效如因用户提供的资料错误、不完整、过时或误导性信息导致EVUS申请失败、延误或被拒绝本公司不承担任何责任。</p>
<div class="highlight">
<p>用户声明并保证:所提供的所有信息均属真实且准确;拥有合法权利提供这些信息;这些信息不侵犯任何第三方的权利;且使用本服务不违反任何适用的法律法规。</p>
</div>
</div>
</div>
<div class="term-item">
<h5 class="term-title">
<span class="term-number">4</span>
公司责任限制
</h5>
<div class="term-content">
<p>本公司提供的是EVUS申请辅助服务并非官方申请渠道。EVUS登记的结果完全由美国海关与边境保护局CBP决定本公司无法影响或控制审批结果。</p>
<p>因此本公司不对EVUS申请结果承担任何保证或承诺。无论申请成功与否已支付的服务费用均不予退还因为服务在提交申请后已经完成。</p>
<div class="highlight">
<p>责任限制:在法律允许的最大范围内,本公司及其员工、代理人不承担因使用本服务而产生的任何间接、附带、特殊、后果性损害或利润损失。我们的最大责任限于用户支付的服务费用金额。</p>
</div>
</div>
</div>
<div class="term-item">
<h5 class="term-title">
<span class="term-number">5</span>
争议解决
</h5>
<div class="term-content">
<p>本服务条款以及任何因使用本服务而产生的争议均适用美国加利福尼亚州法律,不考虑其法律冲突规定。</p>
<p>任何与本服务条款相关的争议应首先通过友好协商解决。若协商不成,双方同意将争议提交至加利福尼亚州洛杉矶郡有管辖权的法院解决。</p>
<div class="highlight">
<p>用户同意:无论位于何处,使用本服务即表示同意接受加利福尼亚州法院的专属管辖,并放弃对此类法院管辖权和地点的任何异议。</p>
</div>
</div>
</div>
</div></div>',
'privacy_policy_info' => ' <div style="padding: 20px;"><div class="section" id="intro">
<p>ITS INTERNATIONAL TRAVEL SERVICE COMPANY以下简称"公司"、"我们"、"我们的")坚定地致力于监管合规以及个人数据的隐私和保护。因此,在本隐私政策中,用户(以下简称"用户"、"您"、"您的")将找到所有相关信息,以更好地了解我们如何处理您的个人数据。</p>
<p>我们可能会根据新的立法或司法要求和/或业务需求等更新本隐私政策。本隐私政策的任何更新或修改将被视为自其在网站上发布之日起适用。因此,建议用户定期查看本隐私政策。</p>
<p>请记住,要访问和使用我们的网站,您必须是成年人并具有签订合同的法律能力,根据您出生或居住国适用的国家法律和法规。</p>
<p>在不影响您在适用法律下的权利的情况下,本隐私政策不是合同或您与我们的合同的一部分。本隐私政策将始终在本网站上提供,以便数据主体可以随时查阅。</p>
</div>
<div class="section" id="data-definition">
<h4>2. 个人数据的定义</h4>
<p>就本隐私政策而言,"个人数据"是指识别数据主体或可用于识别他/她的所有数据例如您在填写申请表时提供的数据以便获取或更新前往美国进行EVUS登记。此外我们会在联系我们的客户服务团队时处理您的个人数据以及处理您在浏览本网站时或从您的IP地址提供的其他信息。</p>
</div>
<div class="section" id="data-controller">
<h4>3. 数据控制者</h4>
<p>本公司如我们在条款和条件中所述将是从您那里收集的任何个人数据的控制者包括但不限于当您浏览我们的网站时填写您的申请表以获得或更新EVUS登记和或您通过电子邮件联系我们的客户服务团队。</p>
</div>
<div class="section" id="scope">
<h4>4. 适用范围</h4>
<p>本隐私政策适用于我们作为我们网站的用户和/或客户从您那里收集、使用和处理的个人数据。</p>
<p>我们的网站可能包含可能将您重定向到第三方网站的功能或链接。在这种情况下,请注意这些第三方网站并非由我们运营,因此我们不对它们提供的信息和内容承担任何责任,也不对这些第三方提供的功能和/或服务的正确提供承担任何责任因为我们无法控制这一点。这些第三方网站受其自己的cookie和隐私政策的约束。</p>
</div>
<div class="section" id="data-collection">
<h4>5. 信息收集</h4>
<p>本公司可能会收集您的姓名、护照信息、联系方式及支付信息,仅用于 EVUS 登记服务。</p>
<p>我们收集以下类别的个人数据:</p>
<p>1、 直接互动例如当您在我们的网站上完成获取或更新EVUS登记的申请时或者当我们根据美国领事馆或大使馆的要求要求您提供其他信息时或者当您填写我们的联系表时或者当您可以通过电子邮件直接联系我们的客户服务团队。</p>
<p>2、 自动化技术,我们可能会通过使用我们自己的或第三方的 cookie 自动收集您的 IP 地址、您在我们网站上查阅的部分或您在我们网站上停留的时间等信息。您可以咨询我们的Cookie 政策获取有关在我们网站上使用cookie、其目的以及其他感兴趣的信息的更多信息。</p>
<p>3、 付款数据,我们只会在付款页面上要求您提供与我们服务费用相关的信用卡或借记卡信息。捕获的数据将安全地发送给我们认证的支付服务提供商,以授权相应的交易。在任何情况下,我们都不会可视化或访问您信用卡或借记卡的完整数据。如果客户通过非本公司授权的渠道输入、提供或发送他/她的卡信息,则由客户负责。</p>
<div class="highlight">
<p>在所有情况下,我们仅处理为实现上述目的所必需的个人数据。从这个意义上说,如果您向我们提供的信息超出了明确要求的信息,则您同意出于您发送信息的知情目的处理所述信息。</p>
</div>
<p>为了能够在现在和之后的任何时间正确使用我们的服务,您必须向我们提供有关您当前个人情况的准确、真实、最新和完整的信息。</p>
<p>如果您向我们提供第三方的个人数据,无论是来自未成年人还是成年人,您确认您已告知他们处理其个人数据的目的,并且您保证您已获得他们的事先明确同意将他们的个人数据传达给我们。从这个意义上说,如果您向我们提供未成年人的个人数据,则您保证您是父母和/或法定监护人,因此,您同意处理您孩子/被监护人的个人数据。</p>
</div>
<div class="section" id="data-usage">
<h4>6. 信息使用</h4>
<p>您的信息仅用于处理 EVUS登记和客户服务不会出售或擅自共享。</p>
<p>请注意,您只需提供填写您选择的申请表所必需的个人数据和信息,以便获取和/或通过我们的联系表格。从这个意义上说,如果您向我们提供的信息超出了明确要求的信息,则表示您同意出于您发送信息的目的对其进行处理。</p>
<p>我们将您的个人数据用于以下目的:</p>
<ul>
<li>为了向您提供我们的服务,并且在必要时,如果所提供的信息/文件不完整和/或错误,我们也可能会要求您提供其他信息/文件。</li>
<li>处理您发送给我们客户服务部门的信息和/或退款查询。</li>
<li>向您发送有关我们服务的运营电子邮件,例如付款确认电子邮件。</li>
<li>用于与处理和管理所提供服务相关的管理目的,例如会计和计费任务以及验证您的付款信息。</li>
<li>遵守我们的法律义务、法律要求、法律和法规,和/或响应司法、警察或其他当局的要求。</li>
<li>保护、调查和防止在本网站上或通过本网站进行的欺诈、未经授权或非法活动。</li>
<li>改善我们的网站、服务和客户支持,并通过分析您在浏览本网站时的行为来增强您在我们网站上的用户体验。</li>
</ul>
</div>
<div class="section" id="legal-basis">
<h4>7. 法律依据</h4>
<p>我们仅在有法律依据的情况下处理您的个人数据。法律依据将取决于我们收集和处理您的个人数据的原因。在几乎所有情况下,法律依据都是:</p>
<ul>
<li>您同意我们处理与收集您的数据时告知的目的相关的您的个人数据(即,在您联系我们的客户服务团队时回复您的信息查询)。</li>
<li>在购买我们的服务或向您发送操作电子邮件时执行合同,以便在向您发送付款确认电子邮件时让您了解最新情况。</li>
<li>遵守我们的法律义务和/或满足行政、司法、警察或相应当局的官方要求。</li>
<li>我们在以下方面的合法权益i在您访问和使用本网站时提供增强的用户体验并有效地运行它ii验证客户的身份并监控欺诈活动以维护我们网站的安全性和完整性iii在对我们的专业服务提出索赔的情况下捍卫和解决我们的权利。</li>
</ul>
</div>
<div class="section" id="data-retention">
<h4>8. 数据保留</h4>
<p>信息将存储在安全服务器上,保存期限不超过申请完成后 12 个月。</p>
<p>我们只会保留您的个人数据,以实现其处理目的所必需的时间,除此之外,您的个人数据将被保留并被适当阻止,直到公司遵守其法律义务并已满足其业务需求、目标和战略。从这个意义上说,您的个人数据将被保留,例如,为了解决和捍卫我们与所提供服务有关的任何索赔的合法利益;直到您的信息和/或退款请求得到答复完成对检测到的欺诈或非法活动的调查如果您不撤销其同意我们将保留Cookie数据。</p>
<p>一旦必要的保留期结束,您的个人数据将从我们的信息系统中安全删除。</p>
<p>此外,如果您同意我们为特定目的处理您的个人数据,请注意您可以随时撤回该同意。收到您的提款请求后,我们将继续删除您的个人数据。</p>
</div>
<div class="section" id="data-sharing">
<h4>9. 数据共享</h4>
<p>必要时,我们可能会将您的信息共享给支付服务提供商或 EVUS 系统。</p>
<p>我们可能会将您的个人数据传达给以下第三方,包括但不限于:</p>
<ul>
<li>美国领事馆或大使馆提交申请</li>
<li>信用卡和借记卡公司处理付款并(在必要时)进行欺诈检查</li>
<li>公共机构和/或行政或司法机关,以及警察,在适用法律法规要求的情况下</li>
<li>律师事务所回应与我们的服务有关的任何索赔和/或保护我们的合法利益</li>
<li>托管我们的信息系统和/或向我们提供技术、工具或支持的技术服务提供商</li>
<li>协助我们正确提供服务的供应商</li>
</ul>
<p>上述第三方的总部可能位于欧洲经济区内;因此,您的个人数据不会受到国际转移。在这方面,我们通知您,我们只会向定居在提供足够数据保护水平的国家/地区的接收者进行个人数据国际传输,否则,我们将采用当前适用法律和法规要求的适当保护措施关于保护个人资料,以确保您的个人资料得到妥善保护,您的个人权利和自由得到保障。</p>
</div>
<div class="section" id="data-security">
<h4>10. 数据安全</h4>
<p>您的信任对我们非常重要。因此,您的个人数据将保密且安全地存储在我们的信息系统中。我们已经制定了适当的技术和组织措施来保护和保护您的个人数据免遭非法或未经授权的访问、丢失或意外破坏、损坏、使用以及非法或未经授权的披露。</p>
<p>同样,我们已采取合理的预防措施,以保证我们的所有员工以及有权访问客户个人数据的供应商或合作者都接受过有关处理所收集的个人数据的充分培训,并且他们遵守上述数据保护义务。</p>
<p>尽管我们将尽最大努力保证您通过我们的网站传输的个人数据和/或您可能通过电子邮件等其他方式提供给我们的个人数据的保护,但请记住,您还应采取预防措施来保存您的个人数据安全例如,您不得通过虚假信息和/或网站输入或确认银行详细信息。</p>
</div>
<div class="section" id="user-rights">
<h4>11. 用户权利</h4>
<p>您有权要求查看、更正或删除您的个人信息。</p>
<p>您可以通过我们的联系表行使您的权利。您可以随时修改网络浏览器的"隐私设置"以设置跟踪cookie。此外您可以在浏览器中安装程序或插件称为"不跟踪"工具这将允许您选择您想要允许的Cookie。</p>
<p>通常我们会在一1个月内回复数据保护权利请求。有时考虑到您的请求是否特别复杂或者您是否提出了多个请求此期限可能会在必要时再延长两2个月。在这种情况下我们会通知您此事件并向您解释延迟的原因。</p>
<p>最后,我们通知您,在您认为适当的情况下,您有权向相应的监管机构提出投诉,尤其是在您认为您的数据保护权利没有得到充分解决的情况下。</p>
</div>
<div class="section" id="cookies">
<h4>12. Cookie政策</h4>
<p>在您未拒绝接受cookies的情况下本应用会在您的计算机上设定或取用cookies以便您能登录或使用依赖于cookies的本应用平台服务或功能。本应用使用cookies可为您提供更加周到的个性化服务包括推广服务。</p>
<p>您有权选择接受或拒绝接受cookies。您可以通过修改浏览器设置的方式拒绝接受cookies。但如果您选择拒绝接受cookies则您可能无法登录或使用依赖于cookies的本应用网络服务或功能。</p>
<p>通过本应用所设cookies所取得的有关信息将适用本政策。</p>
</div>
<div class="section" id="updates">
<h4>13. 政策更新</h4>
<p>如果决定更改隐私政策,我们会在本政策中、本公司网站中以及我们认为适当的位置发布这些更改,以便您了解我们如何收集、使用您的个人信息,哪些人可以访问这些信息,以及在什么情况下我们会透露这些信息。</p>
<p>本公司保留随时修改本政策的权利,因此请经常查看。如对本政策作出重大更改,本公司会通过网站通知的形式告知。</p>
</div>
<div class="section" id="contact">
<h4>14. 联系我们</h4>
<div class="contact-info">
<p>如果对本隐私政策有任何疑问或疑问,您可以联系我们的客户服务团队。</p>
<p>如您发现自己的个人信息泄密,尤其是本应用用户名及密码发生泄露,请您立即联络本应用客服,以便本应用采取相应措施。</p>
</div>
</div></div>',
'disclaimer_info' => '<div style="padding: 20px;">
<div class="disclaimer-item">
<h4>1. 非官方性质声明</h4>
<p>本网站并非美国政府官方网站也不隶属于美国海关与边境保护局CBP。我们是独立的服务提供商专门协助申请人完成EVUS登记流程。</p>
</div>
<div class="disclaimer-item">
<h4>2. 服务范围说明</h4>
<p>ITS INTERNATIONAL TRAVEL SERVICE COMPANY 仅提供 EVUS 表格填写协助和相关技术支持服务。我们不对最终审批结果作出任何保证或承诺,因为审批权完全在于美国政府相关部门。</p>
</div>
<div class="disclaimer-item">
<h4>3. 审批权限声明</h4>
<p>EVUS申请是否获批完全由美国政府相关部门决定我们无法影响或加快审批进程。我们的服务仅限于协助您正确填写和提交申请表格。</p>
</div>
<div class="note">
<p>使用本服务即表示您已阅读、理解并同意上述免责声明。</p>
</div>
</div>
</div>',
'refund_policy_info' => ' <article class="card">
<h1>ITS INTERNATIONAL TRAVEL SERVICE COMPANY客户退款政策</h1>
<p class="intro">提供让客户满意的专业协助服务是我们的一贯宗旨。如果您对我们的服务不满意,我们可根据以下政策为您提供退款申请渠道。</p>
<section>
<h2>一、申请流程与时效</h2>
<p>为确保您的诉求得到及时、妥善的解决,请务必遵循以下申请流程:</p>
<ul>
<li><strong>唯一申请渠道:</strong>所有退款申请必须通过我们的官方联系表格提交,并清晰说明申请原因。请避免通过外部支付机构发起争议,否则可能会中止我们的审核流程并导致退款严重延期。</li>
<li><strong>常规申请时效:</strong>客户须在收到付款成功确认邮件后的 30日内 提出申请。逾期概不受理(仅在极其特殊的不可抗力情况下,本公司方视情况酌情评估)。</li>
<li><strong>批量订单处理:</strong>若您的订单包含多份电子旅行证件申请,请务必为每笔证件单独填写退款表单。</li>
</ul>
</section>
<section>
<h2>二、退款资格与手续费说明</h2>
<p>凡客户成功提交订单,我司系统与人工团队即会开展相应的处理工作。因此,针对所有退款申请,我们将严格执行以下扣款与退款标准:</p>
<ul>
<li><strong>基础服务费扣除(重要):</strong>一旦订单提交成功,若您的退款申请最终获批,我们将统一扣除订单总额的 10% 作为基础处理与人工服务费用,仅退还剩余款项。</li>
<li>
<strong>符合退款条件(扣除 10% 后退还余款):</strong>
<ul class="sub-list">
<li>在提交退款请求当日,您的电子旅行证件申请尚未向政府相关部门正式提交。</li>
<li>您的电子旅行证件申请最终被政府主管部门拒绝。</li>
</ul>
</li>
<li>
<strong>不可退款的情况:</strong>
<ul class="sub-list">
<li><strong>全额服务费:</strong>若您的证件申请已正式进入处理阶段,本公司的专业协助服务费将不予退还。</li>
<li><strong>政府费用:</strong>政府在核准申请时收取的评估费用一经产生,不予退还。</li>
<li><strong>常规附加服务:</strong>附加服务费用(如加急处理费等)一经启动,恕不退还。</li>
</ul>
</li>
</ul>
</section>
<section>
<h2>三、审核与到账时间</h2>
<ul>
<li><strong>审核周期:</strong>我们将在收到您表单后的 24小时内 完成审核,无论结果如何,均会通过电子邮件形式向您发送书面通知。</li>
<li><strong>退款处理:</strong>退款申请一经获批,我们将在 72小时内 循客户原支付方式原路退回款项。</li>
<li><strong>到账时间:</strong>实际资金到账通常需要 10 至 15 个工作日。此过程受制于发卡银行或第三方支付机构的结算周期,并非我方所能控制,敬请谅解。</li>
</ul>
</section>
<section class="contact">
<h2>四、联系我们</h2>
<p>如您对退款政策有任何疑问,或需要进一步的协助,欢迎随时联系我们的客服团队:</p>
<ul>
<li><strong>电子邮件:</strong><a href="mailto:info@itsvisa.com">info@itsvisa.com</a></li>
</ul>
</section>
<section>
<h2 style="color: red">五、法律维权与管辖声明</h2>
<p style="color: red">本公司始终秉承诚信经营原则,致力于为客户提供优质协助。上述声明内容及本条款均受美国法律保护。针对任何恶意投诉、滥用退款流程、侵犯我司合法利益并影响公司正常运行的行为,我司将保留追究权利,并坚决发起跨国诉讼依法维权。</p>
</section>
</article>
',
'cookie_policy_info' => ' <div style="padding: 20px;">
<div class="cookie-notice">
<h3>Cookie使用说明</h3>
<p>本网站由 ITS INTERNATIONAL TRAVEL SERVICE COMPANY 运营,使用 Cookies 技术以提升用户体验。</p>
<h4>我们使用Cookies的主要目的</h4>
<ul>
<li>记住您的登录状态和偏好设置</li>
<li>分析网站访问量和用户行为模式</li>
<li>优化网站性能和改进服务质量</li>
<li>提供个性化的内容推荐</li>
</ul>
<h4>Cookie管理</h4>
<p>您可以通过浏览器设置限制或禁用Cookies。请注意如果禁用Cookies部分网站功能可能无法正常使用包括但不限于</p>
<ul>
<li>无法保持登录状态,需要每次访问时重新登录</li>
<li>个性化设置无法保存</li>
<li>某些服务可能完全无法使用</li>
</ul>
<p>继续使用本网站即表示您同意我们按照本说明使用Cookies技术。</p>
</div>
</div>'
],
];

View File

@@ -0,0 +1,81 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2021 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace app\log\driver;
use think\App;
use think\contract\LogHandlerInterface;
/**
* 本地化调试输出到文件
*/
class Monolog implements LogHandlerInterface
{
/**
* 配置参数
* @var array
*/
protected $config = [
'time_format' => 'c',
'json_options' => JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
];
/**
* 日志写入接口
* @access public
* @param array $log 日志信息
* @return bool
*/
public function save(array $log): bool
{
$info = [];
// 日志信息封装
$time = \DateTime::createFromFormat('0.u00 U', microtime())->setTimezone(new \DateTimeZone(date_default_timezone_get()))->format($this->config['time_format']);
foreach ($log as $type => $val) {
$message = [];
foreach ($val as $msg) {
$message[] = json_encode(['time' => $time, 'type' => $type, 'msg' => $msg], $this->config['json_options']) ;
}
$info[$type] = $message;
}
if ($info) {
return $this->write($info);
}
return true;
}
/**
* 日志写入
* @access protected
* @param array $message 日志信息
* @param string $destination 日志文件
* @return bool
*/
protected function write(array $message): bool
{
$tcp_log_url = env('TCP_LOG_URL');
$tcp_log_port = intval(env('TCP_LOG_PORT'));
$project = env('PROJECT_NAME');
$socket = @fsockopen($tcp_log_url,$tcp_log_port ,$errno, $errstr, 1);
if ($socket) {
fwrite($socket, json_encode(['message' => $message, 'project' => $project], $this->config['json_options']));
fclose($socket);
}
//
return true;
}
}

11
app/middleware.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
// 全局中间件定义文件
return [
// 全局请求缓存
// \think\middleware\CheckRequestCache::class,
// 多语言加载
\think\middleware\LoadLangPack::class,
// Session初始化
\think\middleware\SessionInit::class,
\app\middleware\LogRecorder::class,
];

View File

@@ -0,0 +1,41 @@
<?php
declare (strict_types = 1);
namespace app\middleware;
use think\facade\Log;
class LogRecorder
{
/**
* 处理请求
*
* @param \think\Request $request
* @param \Closure $next
* @return Response
*/
public function handle($request, \Closure $next)
{
// 记录请求信息
$requestInfo = [
'time' => date('Y-m-d H:i:s'),
'method' => $request->method(),
'uri' => $request->url(),
'params' => $request->param(),
'headers' => $request->header(),
'ip' => $request->ip(),
];
Log::write($requestInfo, 'request');
// 继续处理请求
$response = $next($request);
// 记录响应信息
$responseInfo = [
'status' => $response->getCode(),
'data' => $response->getContent(),
];
Log::write($responseInfo, 'response');
return $response;
}
}

9
app/provider.php Normal file
View File

@@ -0,0 +1,9 @@
<?php
use app\ExceptionHandle;
use app\Request;
// 容器Provider定义文件
return [
'think\Request' => Request::class,
'think\exception\Handle' => ExceptionHandle::class,
];

9
app/service.php Normal file
View File

@@ -0,0 +1,9 @@
<?php
use app\AppService;
// 系统服务定义文件
// 服务在完成全局初始化之后执行
return [
AppService::class,
];

View File

@@ -0,0 +1,126 @@
<?php
namespace app\service;
use Aws\S3\S3Client;
use think\facade\Log;
use think\File;
class AwsUploadService
{
/**
* 处理表单文件上传并转发到目标服务器
*
* @param string $fileField 表单文件字段名
* @param array $extraFields 额外传递的字段
* @return array 包含上传结果的数组
*/
public function uploadAndForward( File $file, string $fileField,array $extraFields = []): array
{
$mimeType = $file->getMime();
$s3config = config('filesystem.disks.s3');
$s3 = new S3Client($s3config);
try {
$config = config('app.upload_conf');
$configs=[];
foreach ($config as $key=>$con){
$configs[] = $key.':'.$con;
}
// 验证文件
$validate = validate([
$fileField =>implode('|',$configs),
]);
if (!$validate->check([$fileField => $file])) {
return $this->createErrorResult($validate->getError());
}
// 根据文件类型设置 Content-Type
if (in_array($mimeType, config('app.imageMimeTypes'))) {
$contentType = $mimeType; // 使用图片的实际 MIME 类型
} else {
$contentType = 'application/octet-stream'; // 非图片文件使用默认类型
}
$checkBucket = $this->checkBucket($s3, $extraFields['Bucket']);
if (!$checkBucket) {
return $this->createErrorResult('Bucket error');
}
$s3->putObject([
'Bucket' => $extraFields['Bucket'],
'Key' => $extraFields['Key'],
'Body' => fopen($file->getRealPath(), 'r'),
'ACL' => 'public-read',
'ContentType' => $contentType, // 设置检测到的 Content-Type
]);
return [
'code' => 1,
'data' =>[
'savename'=>$s3config['endpoint'].'/'.$extraFields['Bucket'].'/'.$extraFields['Key']
] ,
];
} catch (\Exception $e) {
return $this->createErrorResult('上传过程中发生异常: ' . $e->getMessage());
}
}
public function checkBucket($s3, $bucket)
{
try {
$buckets = $s3->listBuckets()->get('Buckets');
$buckets_arr = array_column($buckets, 'Name');
if (!in_array($bucket, $buckets_arr)) {
$s3->createBucket([
'Bucket' => $bucket,
]);
$policy = [
'Version' => '2012-10-17',
'Statement' => [
[
'Effect' => 'Allow',
'Principal' => ['AWS' => ['*']],
'Action' => [
's3:GetBucketLocation',
's3:ListBucket',
's3:ListBucketMultipartUploads',
],
'Resource' => "arn:aws:s3:::$bucket",
],
[
'Effect' => 'Allow',
'Principal' => ['AWS' => ['*']],
'Action' => [
's3:DeleteObject',
's3:GetObject',
's3:ListMultipartUploadParts',
's3:PutObject',
's3:AbortMultipartUpload',
],
'Resource' => "arn:aws:s3:::$bucket/*",
],
],
];
$s3->putBucketPolicy([
'Bucket' => $bucket,
'Policy' => json_encode($policy),
]);
}
return true;
} catch (\Exception $e) {
Log::info('checkBucket error:' . $e->getMessage());
return false;
}
}
/**
* 创建错误结果数组
*
* @param string $message 错误信息
* @return array 错误结果
*/
private function createErrorResult(string $message): array
{
return [
'code' => 0,
'error' => $message
];
}
}

View File

@@ -0,0 +1,189 @@
<?php
namespace app\service;
use Curl\Curl;
class BaiduOcrService
{
private $access_token;
private $access_token_url = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials';
private $ocr_url='https://aip.baidubce.com/rest/2.0/ocr/v1/';
public function __construct()
{
$this->getAccessToken();
}
/**
*
*护照识别
* @param string $fileData 图片的二进制数据
* @return array
*/
function passport($fileData)
{
if ($fileData === false) {
return [
'code' => 0,
'msg' => '无法读取上传的文件',
];
}
if (empty($this->access_token)){
return [
'code' => 0,
'msg' => '无法识别',
];
}
$curl = new Curl();
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
$curl->setOpt(CURLOPT_SSL_VERIFYHOST, 0);
$curl->post($this->ocr_url.'passport'.'?access_token='.$this->access_token, [
'image' => base64_encode($fileData),
]);
if ($curl->error) {
return [
'code' => 0,
'msg' => $curl->errorMessage,
];
} else {
if (isset($curl->response->error_code)){
return [
'code' => 0,
'msg' => '解析失败',
];
}
$datas = json_decode(json_encode($curl->response), true);
return [
'code' => 1,
'data' => parsePassport($datas)
];
}
}
public function getAccessToken()
{
if (file_exists(BAIDUACCESSTOKENDIR.'baidu_access_token')){
$access_token = file_get_contents(BAIDUACCESSTOKENDIR.'baidu_access_token');
$access_token = json_decode($access_token,true);
if ($access_token['expires_time'] > time()){
$this->access_token = $access_token['access_token'];
return [
'code' => 1,
'msg' => "获取成功",
];
}
}
$this->access_token_url = $this->access_token_url.'&client_id='.config('app.baidu_ocr.appkey').'&client_secret='.config('app.baidu_ocr.appsecket');
$curl = new Curl();
$curl->setHeader('Content-Type', 'application/json');
$curl->post($this->access_token_url);
if ($curl->error) {
return [
'code' => 0,
'msg' => $curl->errorMessage,
];
}
//进行存储access_token
$access_token = $curl->response->access_token;
$this->access_token = $access_token;
file_put_contents(BAIDUACCESSTOKENDIR.'baidu_access_token',json_encode(
[
'access_token' => $access_token,
'expires_time' => time() + $curl->response->expires_in - 600,
]
));
return [
'code' => 1,
'msg' => "获取成功",
];
}
/**
*
*身份证识别
* @param string $fileData 图片的二进制数据
* @return array
*/
function identification($fileData)
{
if ($fileData === false) {
return [
'code' => 0,
'msg' => '无法读取上传的文件',
];
}
if (empty($this->access_token)){
return [
'code' => 0,
'msg' => '无法识别',
];
}
$curl = new Curl();
$curl->post($this->ocr_url.'idcard'.'?access_token='.$this->access_token, [
'id_card_side' => "front",
'image' => base64_encode($fileData),
]);
if ($curl->error) {
return [
'code' => 0,
'msg' => $curl->errorMessage,
];
} else {
if (isset($curl->response->error_code)){
return [
'code' => 0,
'msg' => '解析失败',
];
}
$datas = json_decode(json_encode($curl->response), true);
return [
'code' => 1,
'data' => parseIdentification($datas)
];
}
}
/**
*
*护照识别
* @param string $fileData 图片的二进制数据
* @return array 包含压缩后图片信息的数组
*/
function visa($fileData)
{
if ($fileData === false) {
return [
'code' => 0,
'msg' => '无法读取上传的文件',
];
}
if (empty($this->access_token)){
return [
'code' => 0,
'msg' => '无法识别',
];
}
$curl = new Curl();
$curl->post($this->ocr_url.'accurate_basic'.'?access_token='.$this->access_token, [
'image' => base64_encode($fileData),
'language_type'=>'ENG'
]);
if ($curl->error) {
return [
'code' => 0,
'msg' => $curl->errorMessage,
];
} else {
if (isset($curl->response->error_code)){
return [
'code' => 0,
'msg' => '解析失败',
];
}
$datas = json_decode(json_encode($curl->response), true);
return [
'code' => 1,
'data' => parseVisa($datas)
];
}
}
}

View File

@@ -0,0 +1,186 @@
<?php
namespace app\service;
/**
* 压缩二进制流图片
*/
class CompressImgService
{
/**
* 压缩通过表单上传的图片
*
* @param string $fileData 图片的二进制数据
* @param int $quality 压缩质量0-100仅适用于JPEG和WebP
* @param string|null $targetFormat 目标格式('jpeg', 'png', 'webp',默认使用原格式)
* @return array 包含压缩后图片信息的数组
*/
function compress($fileData, $quality = 80, $targetFormat = null)
{
// 读取上传的二进制数据
$imageData = file_get_contents($fileData);
if ($imageData === false) {
return [
'code' => 0,
'message' => '无法读取上传的文件',
];
}
// 获取图片信息
$imageInfo = getimagesizefromstring($imageData);
if (!$imageInfo) {
return [
'code' => 0,
'message' => '无法解析图片信息',
];
}
// 确定原始和目标格式
$originalMimeType = $imageInfo['mime'];
$originalExtension = image_type_to_extension($imageInfo[2], false);
if ($targetFormat === null) {
$targetFormat = $originalExtension;
}
// 创建图片资源
switch (strtolower($originalExtension)) {
case 'jpeg':
case 'jpg':
$image = imagecreatefromstring($imageData);
break;
case 'png':
$image = imagecreatefromstring($imageData);
break;
case 'webp':
$image = imagecreatefromstring($imageData);
break;
default:
return [
'code' => 0,
'message' => '不支持的图片格式',
];
}
if (!$image) {
return [
'code' => 0,
'message' => '无法创建图片资源',
];
}
// 压缩并转换图片
ob_start();
switch (strtolower($targetFormat)) {
case 'jpeg':
case 'jpg':
imagejpeg($image, null, $quality);
$compressedMimeType = 'image/jpeg';
break;
case 'png':
$pngQuality = 9 - floor($quality / 11);
imagepng($image, null, $pngQuality);
$compressedMimeType = 'image/png';
break;
case 'webp':
if (function_exists('imagewebp')) {
imagewebp($image, null, $quality);
$compressedMimeType = 'image/webp';
} else {
imagejpeg($image, null, $quality);
$compressedMimeType = 'image/jpeg';
}
break;
default:
imagedestroy($image);
ob_end_clean();
return [
'code' => 0,
'message' => "不支持的目标格式: {$targetFormat}",
];
}
$compressedData = ob_get_clean();
imagedestroy($image);
// 返回结果
return [
'code' => 1,
'compressed_data' => $compressedData,
'base64' => 'data:' . $compressedMimeType . ';base64,' . base64_encode($compressedData),
];
}
public function compressImage($filePath, $fileSize)
{
// 获取图片信息
$imageInfo = getimagesize($filePath);
if (!$imageInfo) {
return false;
}
$mime = $imageInfo['mime'];
// 根据MIME类型创建图像资源
switch ($mime) {
case 'image/jpeg':
$image = imagecreatefromjpeg($filePath);
break;
case 'image/png':
$image = imagecreatefrompng($filePath);
break;
case 'image/gif':
$image = imagecreatefromgif($filePath);
break;
default:
return false;
}
if (!$image) {
return false;
}
// 计算压缩质量(根据文件大小动态调整)
$originalQuality = 85; // 初始质量
$targetSize = 2.5 * 1024 * 1024; // 目标大小2.5MB
// 如果文件很大,降低初始质量
if ($fileSize > 5 * 1024 * 1024) {
$quality = 75;
} elseif ($fileSize > 10 * 1024 * 1024) {
$quality = 65;
} else {
$quality = $originalQuality;
}
// 创建临时文件
$tempFile = tempnam(sys_get_temp_dir(), 'compressed_');
// 保存压缩后的图片
switch ($mime) {
case 'image/jpeg':
imagejpeg($image, $tempFile, $quality);
break;
case 'image/png':
// PNG使用压缩级别0-9需要转换
$pngQuality = 9 - round(($quality / 100) * 9);
imagepng($image, $tempFile, $pngQuality);
break;
case 'image/gif':
imagegif($image, $tempFile);
break;
}
// 释放内存
imagedestroy($image);
// 检查压缩后文件大小如果仍然大于3MB继续压缩
$compressedSize = filesize($tempFile);
if ($compressedSize > 3 * 1024 * 1024) {
// 递归压缩直到满足要求
return $this->compressImage($tempFile, $compressedSize);
}
return $tempFile;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace app\service;
use Swoole\Client;
class SwooleService
{
private static ?self $instance = null;
private Client $client;
private string $host = '127.0.0.1';
private int $port = 9502;
private float $timeout = 0.5;
// 私有化构造函数
private function __construct()
{
$this->client = new Client(SWOOLE_SOCK_TCP);
if (!$this->client->connect($this->host, $this->port, $this->timeout)) {
throw new \RuntimeException("Cannot connect to Swoole Server {$this->host}:{$this->port}");
}
}
// 获取单例
public static function getInstance(): self
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
// 发送任务
public function sendTask(array $task): bool
{
$data = json_encode($task) . "\r\n";
return $this->client->send($data);
}
// 关闭连接
public function close(): void
{
$this->client->close();
self::$instance = null;
}
}

View File

@@ -0,0 +1,133 @@
<?php
namespace app\service;
use think\Exception;
class UploadService
{
/**
* 处理表单文件上传并转发到目标服务器
*
* @param string $fileField 表单文件字段名
* @param string $targetUrl 目标服务器URL
* @param array $extraFields 额外传递的字段
* @param array $headers 额外的请求头
* @return array 包含上传结果的数组
*/
public function uploadAndForward(string $fileField, string $targetUrl, array $extraFields = [], array $headers = []): array
{
// 获取上传的文件
$file = request()->file($fileField);
if (!$file) {
return $this->createErrorResult('未上传文件或上传失败');
}
try {
$config = config('app.upload_conf');
$configs=[];
foreach ($config as $key=>$con){
$configs[] = $key.':'.$con;
}
// 验证文件
$validate = validate([
$fileField =>implode('|',$configs),
]);
if (!$validate->check([$fileField => $file])) {
return $this->createErrorResult($validate->getError());
}
// 使用cURL转发到目标服务器
$result = $this->forwardToTargetServer($file, $targetUrl, $extraFields, $headers);
return $result;
} catch (\Exception $e) {
return $this->createErrorResult('上传过程中发生异常: ' . $e->getMessage());
}
}
/**
* 使用cURL将文件转发到目标服务器
*
* @param \think\File $file 文件对象
* @param string $targetUrl 目标URL
* @param array $extraFields 额外字段
* @param array $headers 请求头
* @return array 包含响应信息的数组
*/
private function forwardToTargetServer(\think\File $file, string $targetUrl, array $extraFields = [], array $headers = []): array
{
$ch = curl_init();
try {
// 设置基本选项
curl_setopt($ch, CURLOPT_URL, $targetUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 300); // 5分钟超时
// 构建上传数据
$postData = [
'file' => new \CURLFile(
$file->getPathname(),
$file->getOriginalMime(),
$file->getOriginalName()
)
];
// 添加额外字段
$postData = array_merge($postData, $extraFields);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
// 设置请求头
$httpHeaders = [];
foreach ($headers as $headerName => $headerValue) {
$httpHeaders[] = "$headerName: $headerValue";
}
if (!empty($httpHeaders)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);
}
// 执行请求
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlInfo = curl_getinfo($ch);
// 检查错误
if ($response === false) {
throw new Exception('cURL错误: ' . curl_error($ch));
}
return [
'code' => 1,
'http_code' => $httpCode,
'response' => $response,
];
} catch (\Exception $e) {
return [
'code' => 0,
'error' => $e->getMessage()
];
} finally {
curl_close($ch);
}
}
/**
* 创建错误结果数组
*
* @param string $message 错误信息
* @return array 错误结果
*/
private function createErrorResult(string $message): array
{
return [
'code' => 0,
'error' => $message
];
}
}